📄 enbitmap.cpp
字号:
// EnBitmap.cpp: implementation of the CEnBitmap class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "EnBitmap.h"
#include <AFXPRIV.H>
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
const int HIMETRIC_INCH = 2540;
enum
{
FT_BMP,
FT_ICO,
FT_JPG,
FT_GIF,
FT_UNKNOWN
};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CEnBitmap::CEnBitmap()
{
}
CEnBitmap::~CEnBitmap()
{
}
BOOL CEnBitmap::LoadImage(UINT uIDRes, LPCTSTR szResourceType, HMODULE hInst, COLORREF crBack)
{
ASSERT(m_hObject == NULL); // only attach once, detach on destroy
if (m_hObject != NULL)
return FALSE;
return Attach(LoadImageResource(uIDRes, szResourceType, hInst, crBack));
}
BOOL CEnBitmap::LoadImage(LPCTSTR szImagePath, COLORREF crBack)
{
//如果原先存在就去除
if (m_hObject != NULL)
{
DeleteObject();
Detach();
}
return Attach(LoadImageFile(szImagePath, crBack));
}
HBITMAP CEnBitmap::LoadImageFile(LPCTSTR szImagePath, COLORREF crBack)
{
int nType = GetFileType(szImagePath);
switch (nType)
{
// the reason for this is that i suspect it is more efficient to load
// bmps this way since it avoids creating device contexts etc that the
// IPicture methods requires. that method however is still valuable
// since it handles other image types and transparency
case FT_BMP:
return (HBITMAP)::LoadImage(NULL, szImagePath, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
case FT_UNKNOWN:
return NULL;
default: // all the rest
{
USES_CONVERSION;
IPicture* pPicture = NULL;
HBITMAP hbm = NULL;
HRESULT hr = OleLoadPicturePath(T2OLE(szImagePath), NULL, 0, crBack, IID_IPicture, (LPVOID *)&pPicture);
if (pPicture)
{
hbm = ExtractBitmap(pPicture, crBack);
pPicture->Release();
}
return hbm;
}
}
return NULL; // can't get here
}
HBITMAP CEnBitmap::LoadImageResource(UINT uIDRes, LPCTSTR szResourceType, HMODULE hInst, COLORREF crBack)
{
BYTE* pBuff = NULL;
int nSize = 0;
HBITMAP hbm = NULL;
// first call is to get buffer size
if (GetResource(MAKEINTRESOURCE(uIDRes), szResourceType, hInst, 0, nSize))
{
if (nSize > 0)
{
pBuff = new BYTE[nSize];
// this loads it
if (GetResource(MAKEINTRESOURCE(uIDRes), szResourceType, hInst, pBuff, nSize))
{
IPicture* pPicture = LoadFromBuffer(pBuff, nSize);
if (pPicture)
{
hbm = ExtractBitmap(pPicture, crBack);
pPicture->Release();
}
}
delete [] pBuff;
}
}
return hbm;
}
IPicture* CEnBitmap::LoadFromBuffer(BYTE* pBuff, int nSize)
{
bool bResult = false;
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, nSize);
void* pData = GlobalLock(hGlobal);
memcpy(pData, pBuff, nSize);
GlobalUnlock(hGlobal);
IStream* pStream = NULL;
IPicture* pPicture = NULL;
if (CreateStreamOnHGlobal(hGlobal, TRUE, &pStream) == S_OK)
{
HRESULT hr = OleLoadPicture(pStream, nSize, FALSE, IID_IPicture, (LPVOID *)&pPicture);
pStream->Release();
}
return pPicture; // caller releases
}
BOOL CEnBitmap::GetResource(LPCTSTR lpName, LPCTSTR lpType, HMODULE hInst, void* pResource, int& nBufSize)
{
HRSRC hResInfo;
HANDLE hRes;
LPSTR lpRes = NULL;
int nLen = 0;
bool bResult = FALSE;
// Find the resource
hResInfo = FindResource(hInst, lpName, lpType);
if (hResInfo == NULL)
return false;
// Load the resource
hRes = LoadResource(hInst, hResInfo);
if (hRes == NULL)
return false;
// Lock the resource
lpRes = (char*)LockResource(hRes);
if (lpRes != NULL)
{
if (pResource == NULL)
{
nBufSize = SizeofResource(hInst, hResInfo);
bResult = true;
}
else
{
if (nBufSize >= (int)SizeofResource(hInst, hResInfo))
{
memcpy(pResource, lpRes, nBufSize);
bResult = true;
}
}
UnlockResource(hRes);
}
// Free the resource
FreeResource(hRes);
return bResult;
}
HBITMAP CEnBitmap::ExtractBitmap(IPicture* pPicture, COLORREF crBack)
{
ASSERT(pPicture);
if (!pPicture)
return NULL;
CBitmap bmMem;
CDC dcMem;
CDC* pDC = CWnd::GetDesktopWindow()->GetDC();
if (dcMem.CreateCompatibleDC(pDC))
{
long hmWidth;
long hmHeight;
pPicture->get_Width(&hmWidth);
pPicture->get_Height(&hmHeight);
int nWidth = MulDiv(hmWidth, pDC->GetDeviceCaps(LOGPIXELSX), HIMETRIC_INCH);
int nHeight = MulDiv(hmHeight, pDC->GetDeviceCaps(LOGPIXELSY), HIMETRIC_INCH);
if (bmMem.CreateCompatibleBitmap(pDC, nWidth, nHeight))
{
CBitmap* pOldBM = dcMem.SelectObject(&bmMem);
if (crBack != -1)
dcMem.FillSolidRect(0, 0, nWidth, nHeight, crBack);
HRESULT hr = pPicture->Render(dcMem, 0, 0, nWidth, nHeight, 0, hmHeight, hmWidth, -hmHeight, NULL);
dcMem.SelectObject(pOldBM);
}
}
CWnd::GetDesktopWindow()->ReleaseDC(pDC);
return (HBITMAP)bmMem.Detach();
}
int CEnBitmap::GetFileType(LPCTSTR szImagePath)
{
CString sPath(szImagePath);
sPath.MakeUpper();
if (sPath.Find(".BMP") > 0)
return FT_BMP;
else if (sPath.Find(".ICO") > 0)
return FT_ICO;
else if (sPath.Find(".JPG") > 0 || sPath.Find(".JPEG") > 0)
return FT_JPG;
else if (sPath.Find(".GIF") > 0)
return FT_GIF;
// else
return FT_UNKNOWN;
}
/////////////////////////////////////////////////////////////////////
// Converts a bitmap to a region
//
// BitmapToRegion : Create a region from the "non-transparent" pixels of a bitmap
// Author : Jean-Edouard Lachand-Robert (http://www.geocities.com/Paris/LeftBank/1160/resume.htm), June 1998.
//
// hBmp : Source bitmap
// cTransparentColor : Color base for the "transparent" pixels (default is black)
// cTolerance : Color tolerance for the "transparent" pixels.
//
// A pixel is assumed to be transparent if the value of each of its 3 components (blue, green and red) is
// greater or equal to the corresponding value in cTransparentColor and is lower or equal to the
// corresponding value in cTransparentColor + cTolerance.
//
HRGN CEnBitmap::BitmapToRegion( COLORREF cTransparentColor ,COLORREF cTolerance)
{
HBITMAP hBmp = (HBITMAP)GetSafeHandle();
HRGN hRgn = NULL;
if(hBmp)
{
// Create a memory DC inside which we will scan the bitmap content
HDC hMemDC = CreateCompatibleDC(NULL);
if(hMemDC)
{
// Get bitmap size
BITMAP bm;
::GetObject(hBmp, sizeof(bm), &bm);
// Create a 32 bits depth bitmap and select it into the memory DC
BITMAPINFOHEADER RGB32BITSBITMAPINFO = {
sizeof(BITMAPINFOHEADER), // biSize
bm.bmWidth, // biWidth;
bm.bmHeight, // biHeight;
1, // biPlanes;
32, // biBitCount
BI_RGB, // biCompression;
0, // biSizeImage;
0, // biXPelsPerMeter;
0, // biYPelsPerMeter;
0, // biClrUsed;
0 // biClrImportant;
};
VOID * pbits32;
HBITMAP hbm32 = CreateDIBSection(hMemDC, (BITMAPINFO *)&RGB32BITSBITMAPINFO, DIB_RGB_COLORS, &pbits32, NULL, 0);
if(hbm32)
{
HBITMAP holdBmp = (HBITMAP)SelectObject(hMemDC, hbm32);
// Create a DC just to copy the bitmap into the memory DC
HDC hDC = CreateCompatibleDC(hMemDC);
if(hDC)
{
// Get how many bytes per row we have for the bitmap bits (rounded up to 32 bits)
BITMAP bm32;
::GetObject(hbm32, sizeof(bm32), &bm32);
while(bm32.bmWidthBytes % 4)
bm32.bmWidthBytes++;
// Copy the bitmap into the memory DC
HBITMAP holdBmp = (HBITMAP)SelectObject(hDC, hBmp);
BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDC, 0, 0, SRCCOPY);
// For better performances, we will use the ExtCreateRegion() function to create the
// region. This function take a RGNDATA structure on entry. We will add rectangles by
// amount of ALLOC_UNIT number in this structure.
#define ALLOC_UNIT 100
DWORD maxRects = ALLOC_UNIT;
HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects));
RGNDATA *pData = (RGNDATA *)GlobalLock(hData);
pData->rdh.dwSize = sizeof(RGNDATAHEADER);
pData->rdh.iType = RDH_RECTANGLES;
pData->rdh.nCount = pData->rdh.nRgnSize = 0;
SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
// Keep on hand highest and lowest values for the "transparent" pixels
BYTE lr = GetRValue(cTransparentColor);
BYTE lg = GetGValue(cTransparentColor);
BYTE lb = GetBValue(cTransparentColor);
BYTE hr = min(0xff, lr + GetRValue(cTolerance));
BYTE hg = min(0xff, lg + GetGValue(cTolerance));
BYTE hb = min(0xff, lb + GetBValue(cTolerance));
// Scan each bitmap row from bottom to top (the bitmap is inverted vertically)
BYTE *p32 = (BYTE *)bm32.bmBits + (bm32.bmHeight - 1) * bm32.bmWidthBytes;
for(int y = 0; y < bm.bmHeight; y++)
{
// Scan each bitmap pixel from left to right
for(int x = 0; x < bm.bmWidth; x++)
{
// Search for a continuous range of "non transparent pixels"
int x0 = x;
LONG *p = (LONG *)p32 + x;
while(x < bm.bmWidth)
{
BYTE b = GetRValue(*p);
if(b >= lr && b <= hr)
{
b = GetGValue(*p);
if(b >= lg && b <= hg)
{
b = GetBValue(*p);
if(b >= lb && b <= hb)
// This pixel is "transparent"
break;
}
}
p++;
x++;
}
if(x > x0)
{
// Add the pixels (x0, y) to (x, y+1) as a new rectangle in the region
if(pData->rdh.nCount >= maxRects)
{
GlobalUnlock(hData);
maxRects += ALLOC_UNIT;
hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), GMEM_MOVEABLE);
pData = (RGNDATA *)GlobalLock(hData);
}
RECT *pr = (RECT *)&pData->Buffer;
SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);
if(x0 < pData->rdh.rcBound.left)
pData->rdh.rcBound.left = x0;
if(y < pData->rdh.rcBound.top)
pData->rdh.rcBound.top = y;
if(x > pData->rdh.rcBound.right)
pData->rdh.rcBound.right = x;
if(y+1 > pData->rdh.rcBound.bottom)
pData->rdh.rcBound.bottom = y+1;
pData->rdh.nCount++;
// On Windows98, ExtCreateRegion() may fail if the number of rectangles is too
// large (ie: > 4000). Therefore, we have to create the region by multiple steps.
if(pData->rdh.nCount == 2000)
{
HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);
if(hRgn)
{
CombineRgn(hRgn, hRgn, h, RGN_OR);
::DeleteObject(h);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -