1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2012, 2014 - TortoiseSVN
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "IconBitmapUtils.h"
24 IconBitmapUtils::IconBitmapUtils()
26 , pfnGetBufferedPaintBits(NULL
)
27 , pfnBeginBufferedPaint(NULL
)
28 , pfnEndBufferedPaint(NULL
)
30 if (SysInfo::Instance().IsVistaOrLater())
32 hUxTheme
= AtlLoadSystemLibraryUsingFullPath(_T("UXTHEME.DLL"));
36 pfnGetBufferedPaintBits
= (FN_GetBufferedPaintBits
)::GetProcAddress(hUxTheme
, "GetBufferedPaintBits");
37 pfnBeginBufferedPaint
= (FN_BeginBufferedPaint
)::GetProcAddress(hUxTheme
, "BeginBufferedPaint");
38 pfnEndBufferedPaint
= (FN_EndBufferedPaint
)::GetProcAddress(hUxTheme
, "EndBufferedPaint");
43 IconBitmapUtils::~IconBitmapUtils()
45 std::map
<UINT
, HBITMAP
>::iterator it
;
46 for (it
= bitmaps
.begin(); it
!= bitmaps
.end(); ++it
)
48 ::DeleteObject(it
->second
);
52 FreeLibrary(hUxTheme
);
55 HBITMAP
IconBitmapUtils::IconToBitmap(HINSTANCE hInst
, UINT uIcon
)
57 std::map
<UINT
, HBITMAP
>::iterator bitmap_it
= bitmaps
.lower_bound(uIcon
);
58 if (bitmap_it
!= bitmaps
.end() && bitmap_it
->first
== uIcon
)
59 return bitmap_it
->second
;
61 HICON hIcon
= (HICON
)LoadImage(hInst
, MAKEINTRESOURCE(uIcon
), IMAGE_ICON
, 12, 12, LR_DEFAULTCOLOR
);
67 rect
.right
= ::GetSystemMetrics(SM_CXMENUCHECK
);
68 rect
.bottom
= ::GetSystemMetrics(SM_CYMENUCHECK
);
70 rect
.left
= rect
.top
= 0;
72 HWND desktop
= ::GetDesktopWindow();
79 HDC screen_dev
= ::GetDC(desktop
);
80 if (screen_dev
== NULL
)
86 // Create a compatible DC
87 HDC dst_hdc
= ::CreateCompatibleDC(screen_dev
);
91 ::ReleaseDC(desktop
, screen_dev
);
95 // Create a new bitmap of icon size
96 HBITMAP bmp
= ::CreateCompatibleBitmap(screen_dev
, rect
.right
, rect
.bottom
);
101 ::ReleaseDC(desktop
, screen_dev
);
105 // Select it into the compatible DC
106 HBITMAP old_dst_bmp
= (HBITMAP
)::SelectObject(dst_hdc
, bmp
);
107 if (old_dst_bmp
== NULL
)
111 ::ReleaseDC(desktop
, screen_dev
);
115 // Fill the background of the compatible DC with the white color
116 // that is taken by menu routines as transparent
117 ::SetBkColor(dst_hdc
, RGB(255, 255, 255));
118 ::ExtTextOut(dst_hdc
, 0, 0, ETO_OPAQUE
, &rect
, NULL
, 0, NULL
);
120 // Draw the icon into the compatible DC
121 ::DrawIconEx(dst_hdc
, 0, 0, hIcon
, rect
.right
, rect
.bottom
, 0, NULL
, DI_NORMAL
);
124 ::SelectObject(dst_hdc
, old_dst_bmp
);
126 ::ReleaseDC(desktop
, screen_dev
);
129 bitmaps
.insert(bitmap_it
, std::make_pair(uIcon
, bmp
));
133 HBITMAP
IconBitmapUtils::IconToBitmapPARGB32(HINSTANCE hInst
, UINT uIcon
)
135 std::map
<UINT
, HBITMAP
>::iterator bitmap_it
= bitmaps
.lower_bound(uIcon
);
136 if (bitmap_it
!= bitmaps
.end() && bitmap_it
->first
== uIcon
)
137 return bitmap_it
->second
;
139 HICON hIcon
= (HICON
)LoadImage(hInst
, MAKEINTRESOURCE(uIcon
), IMAGE_ICON
, 16, 16, LR_DEFAULTCOLOR
);
141 HBITMAP hBmp
= IconToBitmapPARGB32(hIcon
);
146 bitmaps
.insert(bitmap_it
, std::make_pair(uIcon
, hBmp
));
151 HBITMAP
IconBitmapUtils::IconToBitmapPARGB32(HICON hIcon
)
156 if (pfnBeginBufferedPaint
== NULL
|| pfnEndBufferedPaint
== NULL
|| pfnGetBufferedPaintBits
== NULL
)
160 sizIcon
.cx
= GetSystemMetrics(SM_CXSMICON
);
161 sizIcon
.cy
= GetSystemMetrics(SM_CYSMICON
);
164 SetRect(&rcIcon
, 0, 0, sizIcon
.cx
, sizIcon
.cy
);
167 HDC hdcDest
= CreateCompatibleDC(NULL
);
170 if (SUCCEEDED(Create32BitHBITMAP(hdcDest
, &sizIcon
, NULL
, &hBmp
)))
172 HBITMAP hbmpOld
= (HBITMAP
)SelectObject(hdcDest
, hBmp
);
175 BLENDFUNCTION bfAlpha
= { AC_SRC_OVER
, 0, 255, AC_SRC_ALPHA
};
176 BP_PAINTPARAMS paintParams
= {0};
177 paintParams
.cbSize
= sizeof(paintParams
);
178 paintParams
.dwFlags
= BPPF_ERASE
;
179 paintParams
.pBlendFunction
= &bfAlpha
;
182 HPAINTBUFFER hPaintBuffer
= pfnBeginBufferedPaint(hdcDest
, &rcIcon
, BPBF_DIB
, &paintParams
, &hdcBuffer
);
185 if (DrawIconEx(hdcBuffer
, 0, 0, hIcon
, sizIcon
.cx
, sizIcon
.cy
, 0, NULL
, DI_NORMAL
))
187 // If icon did not have an alpha channel we need to convert buffer to PARGB
188 ConvertBufferToPARGB32(hPaintBuffer
, hdcDest
, hIcon
, sizIcon
);
191 // This will write the buffer contents to the destination bitmap
192 pfnEndBufferedPaint(hPaintBuffer
, TRUE
);
195 SelectObject(hdcDest
, hbmpOld
);
205 HRESULT
IconBitmapUtils::Create32BitHBITMAP(HDC hdc
, const SIZE
*psize
, __deref_opt_out
void **ppvBits
, __out HBITMAP
* phBmp
) const
216 SecureZeroMemory(&bmi
, sizeof(bmi
));
217 bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
218 bmi
.bmiHeader
.biPlanes
= 1;
219 bmi
.bmiHeader
.biCompression
= BI_RGB
;
221 bmi
.bmiHeader
.biWidth
= psize
->cx
;
222 bmi
.bmiHeader
.biHeight
= psize
->cy
;
223 bmi
.bmiHeader
.biBitCount
= 32;
225 HDC hdcUsed
= hdc
? hdc
: GetDC(NULL
);
228 *phBmp
= CreateDIBSection(hdcUsed
, &bmi
, DIB_RGB_COLORS
, ppvBits
, NULL
, 0);
231 ReleaseDC(NULL
, hdcUsed
);
234 return (NULL
== *phBmp
) ? E_OUTOFMEMORY
: S_OK
;
237 HRESULT
IconBitmapUtils::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer
, HDC hdc
, HICON hicon
, SIZE
& sizIcon
)
241 HRESULT hr
= pfnGetBufferedPaintBits(hPaintBuffer
, &prgbQuad
, &cxRow
);
244 Gdiplus::ARGB
*pargb
= reinterpret_cast<Gdiplus::ARGB
*>(prgbQuad
);
245 if (!HasAlpha(pargb
, sizIcon
, cxRow
))
248 if (GetIconInfo(hicon
, &info
))
252 hr
= ConvertToPARGB32(hdc
, pargb
, info
.hbmMask
, sizIcon
, cxRow
);
255 DeleteObject(info
.hbmColor
);
256 DeleteObject(info
.hbmMask
);
264 bool IconBitmapUtils::HasAlpha(__in
Gdiplus::ARGB
*pargb
, SIZE
& sizImage
, int cxRow
) const
266 ULONG cxDelta
= cxRow
- sizImage
.cx
;
267 for (ULONG y
= sizImage
.cy
; y
; --y
)
269 for (ULONG x
= sizImage
.cx
; x
; --x
)
271 if (*pargb
++ & 0xFF000000)
283 HRESULT
IconBitmapUtils::ConvertToPARGB32(HDC hdc
, __inout
Gdiplus::ARGB
*pargb
, HBITMAP hbmp
, SIZE
& sizImage
, int cxRow
) const
286 SecureZeroMemory(&bmi
, sizeof(bmi
));
287 bmi
.bmiHeader
.biSize
= sizeof(BITMAPINFOHEADER
);
288 bmi
.bmiHeader
.biPlanes
= 1;
289 bmi
.bmiHeader
.biCompression
= BI_RGB
;
291 bmi
.bmiHeader
.biWidth
= sizImage
.cx
;
292 bmi
.bmiHeader
.biHeight
= sizImage
.cy
;
293 bmi
.bmiHeader
.biBitCount
= 32;
295 HANDLE hHeap
= GetProcessHeap();
296 void *pvBits
= HeapAlloc(hHeap
, 0, bmi
.bmiHeader
.biWidth
* 4 * bmi
.bmiHeader
.biHeight
);
298 return E_OUTOFMEMORY
;
300 HRESULT hr
= E_UNEXPECTED
;
301 if (GetDIBits(hdc
, hbmp
, 0, bmi
.bmiHeader
.biHeight
, pvBits
, &bmi
, DIB_RGB_COLORS
) == bmi
.bmiHeader
.biHeight
)
303 ULONG cxDelta
= cxRow
- bmi
.bmiHeader
.biWidth
;
304 Gdiplus::ARGB
*pargbMask
= static_cast<Gdiplus::ARGB
*>(pvBits
);
306 for (ULONG y
= bmi
.bmiHeader
.biHeight
; y
; --y
)
308 for (ULONG x
= bmi
.bmiHeader
.biWidth
; x
; --x
)
318 *pargb
++ |= 0xFF000000;
326 HeapFree(hHeap
, 0, pvBits
);