|
// Copyright (C) 2011 Fog Creek Software
// Copyright (C) 2009 TortoiseSVN
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software Foundation,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// Adapted for use in TortoiseHg by Veniamin Albaev
#include "stdafx.h"
#include "IconBitmapUtils.h"
#include "SysInfo.h"
CIconBitmapUtils::CIconBitmapUtils() :
m_hUxTheme(NULL)
{
if (CSysInfo::IsVistaOrLater())
{
m_hUxTheme = ::LoadLibrary(_T("UXTHEME.DLL"));
if (m_hUxTheme)
{
m_pfnGetBufferedPaintBits = (FN_GetBufferedPaintBits)::GetProcAddress(
m_hUxTheme, "GetBufferedPaintBits");
m_pfnBeginBufferedPaint = (FN_BeginBufferedPaint)::GetProcAddress(m_hUxTheme,
"BeginBufferedPaint");
m_pfnEndBufferedPaint = (FN_EndBufferedPaint)::GetProcAddress(m_hUxTheme,
"EndBufferedPaint");
}
}
}
CIconBitmapUtils::~CIconBitmapUtils()
{
if (m_hUxTheme)
::FreeLibrary(m_hUxTheme);
}
HBITMAP CIconBitmapUtils::IconToBitmap(HICON hIcon)
{
if (!hIcon) return NULL;
RECT rect;
rect.right = ::GetSystemMetrics(SM_CXMENUCHECK);
rect.bottom = ::GetSystemMetrics(SM_CYMENUCHECK);
rect.left = rect.top = 0;
HWND hwndDesktop = ::GetDesktopWindow();
if (hwndDesktop == NULL) return NULL;
HDC hScreenDC = ::GetDC(hwndDesktop);
if (hScreenDC == NULL) return NULL;
// Create a compatible DC
HDC hDestDC = ::CreateCompatibleDC(hScreenDC);
if (hDestDC == NULL)
{
::ReleaseDC(hwndDesktop, hScreenDC);
return NULL;
}
// Create a new bitmap of icon size
HBITMAP hBitmap = ::CreateCompatibleBitmap(hScreenDC, rect.right, rect.bottom);
if (hBitmap == NULL)
{
::DeleteDC(hDestDC);
::ReleaseDC(hwndDesktop, hScreenDC);
return NULL;
}
// Select it into the compatible DC
HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hDestDC, hBitmap);
if (hOldBitmap == NULL) return NULL;
// Fill the background of the compatible DC with the white color
// that is taken by menu routines as transparent
::SetBkColor(hDestDC, RGB(255, 255, 255));
::ExtTextOut(hDestDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL);
// Draw the icon into the compatible DC
::DrawIconEx(hDestDC, 0, 0, hIcon, rect.right, rect.bottom, 0, NULL, DI_NORMAL);
// Restore settings
::SelectObject(hDestDC, hOldBitmap);
::DeleteDC(hDestDC);
::ReleaseDC(hwndDesktop, hScreenDC);
return hBitmap;
}
HBITMAP CIconBitmapUtils::IconToBitmapPARGB32(HICON hIcon)
{
if (!hIcon) return NULL;
if (m_pfnBeginBufferedPaint == NULL || m_pfnEndBufferedPaint == NULL ||
m_pfnGetBufferedPaintBits == NULL)
{
ATLTRACE(" CIconBitmapUtils::IconToBitmapPARGB32: Theme functions not found, "
"returns NULL\n");
return NULL;
}
SIZE sizeIcon;
sizeIcon.cx = ::GetSystemMetrics(SM_CXSMICON);
sizeIcon.cy = ::GetSystemMetrics(SM_CYSMICON);
RECT rcIcon;
::SetRect(&rcIcon, 0, 0, sizeIcon.cx, sizeIcon.cy);
HBITMAP hBitmap = NULL;
HDC hDestDC = ::CreateCompatibleDC(NULL);
if (hDestDC)
{
if (SUCCEEDED(Create32BitHBITMAP(hDestDC, &sizeIcon, NULL, &hBitmap)))
{
HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hDestDC, hBitmap);
if (hOldBitmap)
{
BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
BP_PAINTPARAMS paintParams = {0};
paintParams.cbSize = sizeof(BP_PAINTPARAMS);
paintParams.dwFlags = BPPF_ERASE;
paintParams.pBlendFunction = &bfAlpha;
HDC hBufferDC;
HPAINTBUFFER hPaintBuffer = m_pfnBeginBufferedPaint(hDestDC, &rcIcon,
BPBF_DIB, &paintParams, &hBufferDC);
if (hPaintBuffer)
{
if (::DrawIconEx(hBufferDC, 0, 0, hIcon, sizeIcon.cx, sizeIcon.cy, 0,
NULL, DI_NORMAL))
{
// If icon did not have an alpha channel we need to convert buffer to PARGB
ConvertBufferToPARGB32(hPaintBuffer, hDestDC, hIcon, sizeIcon);
}
// This will write the buffer contents to the destination bitmap
m_pfnEndBufferedPaint(hPaintBuffer, TRUE);
}
::SelectObject(hDestDC, hOldBitmap);
}
}
::DeleteDC(hDestDC);
}
return hBitmap;
}
HRESULT CIconBitmapUtils::Create32BitHBITMAP(HDC hDC, const SIZE *pSize, void **ppvBits,
HBITMAP* phBitmap)
{
*phBitmap = NULL;
BITMAPINFO bmi;
::SecureZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biWidth = pSize->cx;
bmi.bmiHeader.biHeight = pSize->cy;
bmi.bmiHeader.biBitCount = 32;
HDC hUsedDC = hDC ? hDC : ::GetDC(NULL);
if (hUsedDC)
{
*phBitmap = ::CreateDIBSection(hUsedDC, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
if (hDC != hUsedDC)
{
::ReleaseDC(NULL, hUsedDC);
}
}
return (NULL == *phBitmap) ? E_OUTOFMEMORY : S_OK;
}
HRESULT CIconBitmapUtils::ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hDC,
HICON hIcon, SIZE& sizeIcon)
{
RGBQUAD *prgbQuad;
int cxRow;
HRESULT hr = m_pfnGetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow);
if (SUCCEEDED(hr))
{
DWORD* pargb = (DWORD*)prgbQuad;
if (!HasAlpha(pargb, sizeIcon, cxRow))
{
ICONINFO info;
if (::GetIconInfo(hIcon, &info))
{
if (info.hbmMask)
{
hr = ConvertToPARGB32(hDC, pargb, info.hbmMask, sizeIcon, cxRow);
}
::DeleteObject(info.hbmColor);
::DeleteObject(info.hbmMask);
}
}
}
return hr;
}
bool CIconBitmapUtils::HasAlpha(DWORD* pargb, SIZE& sizeImage, int cxRow)
{
ULONG cxDelta = cxRow - sizeImage.cx;
for (ULONG y = sizeImage.cy; y; --y)
{
for (ULONG x = sizeImage.cx; x; --x)
{
if (*pargb++ & 0xFF000000)
{
return true;
}
}
pargb += cxDelta;
}
return false;
}
HRESULT CIconBitmapUtils::ConvertToPARGB32(HDC hDC, DWORD* pargb, HBITMAP hBitmap,
SIZE& sizeImage, int cxRow)
{
BITMAPINFO bmi;
::SecureZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biWidth = sizeImage.cx;
bmi.bmiHeader.biHeight = sizeImage.cy;
bmi.bmiHeader.biBitCount = 32;
HRESULT hr = E_OUTOFMEMORY;
HANDLE hHeap = ::GetProcessHeap();
void *pvBits = ::HeapAlloc(hHeap, 0,
bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight);
if (pvBits)
{
hr = E_UNEXPECTED;
if (::GetDIBits(hDC, hBitmap, 0, bmi.bmiHeader.biHeight, pvBits, &bmi,
DIB_RGB_COLORS) == bmi.bmiHeader.biHeight)
{
ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;
DWORD* pargbMask = (DWORD*)pvBits;
for (ULONG y = bmi.bmiHeader.biHeight; y; --y)
{
for (ULONG x = bmi.bmiHeader.biWidth; x; --x)
{
if (*pargbMask++)
{
// transparent pixel
*pargb++ = 0;
}
else
{
// opaque pixel
*pargb++ |= 0xFF000000;
}
}
pargb += cxDelta;
}
hr = S_OK;
}
::HeapFree(hHeap, 0, pvBits);
}
return hr;
}
|
Loading...