VC只用GDI实现位图展现简单特效

展示截图(略大,4.24M):


Demo下载(需要1积分):http://download.csdn.net/detail/joneeky/7860555


这些把位图加载到内存DC上的代码需要重复使用,于是定义成宏:

#define READY_CODE								\
CGditestDlg *pMainDlg = (CGditestDlg *)pParam;	\
CDC *pDC = pMainDlg->GetDC();					\
CBitmap bmp;									\
if (1 == pMainDlg->m_counter)					\
{												\
	bmp.LoadBitmap(IDB_BITMAP1);				\
	pMainDlg->m_counter = 2;					\
}												\
else if (2 == pMainDlg->m_counter)				\
{												\
	bmp.LoadBitmap(IDB_BITMAP2);				\
	pMainDlg->m_counter = 3;					\
}												\
else											\
{												\
	bmp.LoadBitmap(IDB_BITMAP3);				\
	pMainDlg->m_counter = 1;					\
}												\
CDC dcMem;										\
dcMem.CreateCompatibleDC(pDC);					\
dcMem.SelectObject(&bmp);						\
BITMAP bm;										\
bmp.GetBitmap(&bm);								\
pMainDlg->Invalidate();							\
EnumChildWindows(pMainDlg->GetSafeHwnd(),		\
	EnumChildProc, 0L);

#define CLEAN_CODE			\
dcMem.DeleteDC();			\
bmp.DeleteObject();			\
EnumChildWindows(			\
	pMainDlg->GetSafeHwnd(),\
	EnumChildProc, 1L);		\

#define PI	3.14

//弧度 = 2π * 角度 / 360
#define RADIAN(degree)	((float)((2 * PI * degree) / 360))


/*从上飞入*/
UINT CGditestDlg::FlyIntoFromTop(LPVOID pParam)
{
	READY_CODE
	for (int ySrc = bm.bmHeight; ySrc >= 0; ySrc -= 10)
	{
		pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcMem, 0, ySrc, SRCCOPY);
		Sleep(1);
	}
	CLEAN_CODE
	return 0;
}

/*从左飞入*/
UINT CGditestDlg::FlyIntoFromLeft(LPVOID pParam)
{
	READY_CODE
	for (int xSrc = bm.bmWidth; xSrc >= 0; xSrc -= 10)
	{
		pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcMem, xSrc, 0, SRCCOPY);
		Sleep(1);
	}
	CLEAN_CODE
	return 0;
}

/*从上展开*/
UINT CGditestDlg::UnfoldFromTop(LPVOID pParam)
{
	READY_CODE
	for (int htDes = 0; htDes <= bm.bmHeight; htDes += 10)
	{
		pDC->BitBlt(0, 0, bm.bmWidth, htDes, &dcMem, 0, 0, SRCCOPY);
		Sleep(1);
	}
	CLEAN_CODE
	return 0;
}

/*从左展开*/
UINT CGditestDlg::UnfoldFromLeft(LPVOID pParam)
{
	READY_CODE
	for (int wtDes = 0; wtDes <= bm.bmWidth; wtDes += 10)
	{
		pDC->BitBlt(0, 0, wtDes, bm.bmHeight, &dcMem, 0, 0, SRCCOPY);
		Sleep(1);
	}
	CLEAN_CODE
	return 0;
}


百叶窗就是把位图分块同步显示,原理比较简单。

/*水平百叶窗*/
UINT CGditestDlg::HorizontalWindow(LPVOID pParam)
{
	READY_CODE
	int n = bm.bmHeight / 8;
	for (int htDes = 0; htDes <= n; htDes += 1)
	{
		for (int i = 0; i < 8; i++)
		{
			pDC->BitBlt(0, n * i, bm.bmWidth, htDes, &dcMem, 0, n * i, SRCCOPY);
		}
		Sleep(10);
	}
	CLEAN_CODE
	return 0;
}

/*垂直百叶窗*/
UINT CGditestDlg::VerticalWindow(LPVOID pParam)
{
	READY_CODE
	int n = bm.bmWidth / 8;
	for (int wtDes = 0; wtDes <= n; wtDes += 1)
	{
		for (int i = 0; i < 8; i++)
		{
			pDC->BitBlt(n * i, 0, wtDes, bm.bmHeight, &dcMem, n * i, 0, SRCCOPY);
		}
		Sleep(10);
	}
	CLEAN_CODE
	return 0;
}


拉直其实和百叶窗相差不了多少,也是分块同步显示,但是每一块是慢慢变大的。


/*往下拉直*/
UINT CGditestDlg::StraightenToBottom(LPVOID pParam)
{
	READY_CODE
	int n = bm.bmHeight / 8;
	for (int htDes = 0; htDes <= n; htDes += 1)
	{
		for (int i = 0; i < 8; i++)
		{
			pDC->BitBlt(0, htDes * i, bm.bmWidth, htDes, &dcMem, 0, n * i, SRCCOPY);
		}
		Sleep(10);
	}
	CLEAN_CODE
	return 0;
}

/*往右拉直*/
UINT CGditestDlg::StraightenToRight(LPVOID pParam)
{
	READY_CODE
	int n = bm.bmWidth / 8;
	for (int wtDes = 0; wtDes <= n; wtDes += 1)
	{
		for (int i = 0; i < 8; i++)
		{
			pDC->BitBlt(wtDes * i, 0, wtDes, bm.bmHeight, &dcMem, n * i, 0, SRCCOPY);
		}
		Sleep(10);
	}
	CLEAN_CODE
	return 0;
}


由小变大主要是要保存图像比例和算位图从在哪个坐标显示,图像大小发生变化,坐标随之变化。可以先确定图像的宽,然后 长=宽*(高/2);x坐标=位图实际的宽/2-当前的宽/2,y坐标=位图实际的高/2-当前的高/2。


/*由小变大*/
UINT CGditestDlg::SmallToLarge(LPVOID pParam)
{
	READY_CODE
	float x, y, w, h;
	float base = 0;
	pDC->SetStretchBltMode(HALFTONE);
	while ((int)(base += 10) <= bm.bmWidth)
	{
		w = base;
		h = base * ((float)bm.bmHeight / (float)bm.bmWidth);
		x = (float)bm.bmWidth / 2 - w / 2;
		y = (float)bm.bmHeight / 2 - h / 2;
		pDC->StretchBlt((int)x, (int)y, (int)w, (int)h, &dcMem, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
		Sleep(10);
	}
	CLEAN_CODE
	return 0;
}

旋转变大比较复杂,在上网找到可以完成位图按任意弧度旋转的代码改了一下,使其可以指定显示的大小:

最后一个参数的颜色值,指定图像旋转后空出来的位置填充的颜色

HBITMAP GetRotatedBitmap(HBITMAP hBitmap, float radians, int width, int height, COLORREF clrBack)  
{  
	// Create a memory DC compatible with the display  
	CDC sourceDC, destDC;  
	sourceDC.CreateCompatibleDC( NULL );  
	destDC.CreateCompatibleDC( NULL );  
	
	// Get logical coordinates  
	BITMAP bm, bmOld;  
	::GetObject( hBitmap, sizeof( bm ), &bm );  
	bmOld = bm;
	bm.bmWidth = width;
	bm.bmHeight = height;
	
	float cosine = (float)cos(radians);  
	float sine = (float)sin(radians);  
	
	// Compute dimensions of the resulting bitmap  
	// First get the coordinates of the 3 corners other than origin  
	int x1 = (int)(bm.bmHeight * sine);  
	int y1 = (int)(bm.bmHeight * cosine);  
	int x2 = (int)(bm.bmWidth * cosine + bm.bmHeight * sine);  
	int y2 = (int)(bm.bmHeight * cosine - bm.bmWidth * sine);  
	int x3 = (int)(bm.bmWidth * cosine);  
	int y3 = (int)(-bm.bmWidth * sine);  
	
	int minx = min(0,min(x1, min(x2,x3)));  
	int miny = min(0,min(y1, min(y2,y3)));  
	int maxx = max(0,max(x1, max(x2,x3)));  
	int maxy = max(0,max(y1, max(y2,y3)));  
	
	int w = maxx - minx;  
	int h = maxy - miny;  
	
	// Create a bitmap to hold the result  
	HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), w, h);  
	
	HBITMAP hbmOldSource = (HBITMAP)::SelectObject( sourceDC.m_hDC, hBitmap );  
	HBITMAP hbmOldDest = (HBITMAP)::SelectObject( destDC.m_hDC, hbmResult );  
	
	// Draw the background color before we change mapping mode  
	HBRUSH hbrBack = CreateSolidBrush( clrBack );  
	HBRUSH hbrOld = (HBRUSH)::SelectObject( destDC.m_hDC, hbrBack );  
	destDC.PatBlt( 0, 0, w, h, PATCOPY );  
	::DeleteObject( ::SelectObject( destDC.m_hDC, hbrOld ) );  
	
	// We will use world transform to rotate the bitmap  
	SetGraphicsMode(destDC.m_hDC, GM_ADVANCED);  
	XFORM xform;  
	xform.eM11 = cosine;  
	xform.eM12 = -sine;  
	xform.eM21 = sine;  
	xform.eM22 = cosine;  
	xform.eDx = (float)-minx;  
	xform.eDy = (float)-miny;  
	
	SetWorldTransform( destDC.m_hDC, &xform );  
	
	// Now do the actual rotating - a pixel at a time  
//	destDC.BitBlt(0,0,bm.bmWidth, bm.bmHeight, &sourceDC, 0, 0, SRCCOPY );  
	destDC.SetStretchBltMode(HALFTONE);
	destDC.StretchBlt(0, 0, bm.bmWidth, bm.bmHeight, &sourceDC, 0, 0, bmOld.bmWidth, bmOld.bmHeight, SRCCOPY);
	
	// Restore DCs  
	::SelectObject( sourceDC.m_hDC, hbmOldSource );  
	::SelectObject( destDC.m_hDC, hbmOldDest );  
	
	return hbmResult;  
}


只要是要控制当位图变到最大时旋转角度是0度,也就是说当变到最大时就不能再大了,但是如果角度不是0度也还是要转到0度去。

还有就是我使用了双缓冲,怎么还闪烁得这么厉害我也不清楚了。


/*旋转变大*/
UINT CGditestDlg::LargerWithSpin(LPVOID pParam)
{
//	READY_CODE
	CGditestDlg *pMainDlg = (CGditestDlg *)pParam;
	CDC *pDC = pMainDlg->GetDC();
	pMainDlg->Invalidate();
	EnumChildWindows(pMainDlg->GetSafeHwnd(), EnumChildProc, 0L);
	pDC->SetStretchBltMode(HALFTONE);
	CBitmap bmp;
	if (1 == pMainDlg->m_counter)
	{
		bmp.LoadBitmap(IDB_BITMAP1);
		pMainDlg->m_counter = 2;
	}
	else if (2 == pMainDlg->m_counter)
	{
		bmp.LoadBitmap(IDB_BITMAP2);
		pMainDlg->m_counter = 3;
	}
	else
	{
		bmp.LoadBitmap(IDB_BITMAP3);
		pMainDlg->m_counter = 1;
	}
	HBITMAP hBmp;
	CDC dcMem;
	CBitmap *pbmpRotated;
	BITMAP bm;
	int degree = 0;
	float width, height;
	float base = 0;
	long oldWidth, oldHeight;
	float x, y;
	int lastX = 0, lastY = 0, lastWidth = 0, lastHeight = 0;
	dcMem.CreateCompatibleDC(pDC);
	bmp.GetBitmap(&bm);
	oldWidth = bm.bmWidth;
	oldHeight = bm.bmHeight;
	CRect rcWnd;
	pMainDlg->GetWindowRect(&rcWnd);
	CBitmap bmpBkgd;
	CDC dcBkgd;
	dcBkgd.CreateCompatibleDC(pDC);
	bmpBkgd.CreateCompatibleBitmap(pDC, rcWnd.Width() + 200, rcWnd.Height() + 200);
	dcBkgd.SelectObject(&bmpBkgd);
	dcBkgd.FillSolidRect(0, 0, rcWnd.Width() + 200, rcWnd.Height() + 200, RGB(255, 174 ,201));
	while (1)
	{
		if (base <= oldWidth)
		{
			base += 15;
		}
		if (base > oldWidth && degree == 0)
		{
			break;
		}
		width = base;
		height = base * ((float)oldHeight / (float)oldWidth);
		hBmp = GetRotatedBitmap((HBITMAP)bmp.GetSafeHandle(), RADIAN(degree), (int)width, (int)height, RGB(255, 174 ,201));
		pbmpRotated = CBitmap::FromHandle(hBmp);
		dcMem.SelectObject(pbmpRotated);
		pbmpRotated->GetBitmap(&bm);
		x = (float)oldWidth / 2 - (float)bm.bmWidth / 2;
		y = (float)oldHeight / 2 - (float)bm.bmHeight / 2;
		pDC->BitBlt(lastX, lastY, lastWidth, lastHeight, &dcBkgd, 0, 0, SRCCOPY);
		pDC->BitBlt((int)x, (int)y, bm.bmWidth, bm.bmHeight, &dcMem, 0, 0, SRCCOPY);
		lastX = (int)x;
		lastY = (int)y;
		lastWidth = bm.bmWidth;
		lastHeight = bm.bmHeight;
		pbmpRotated->DeleteObject();
		degree = (degree += 15) > 360 ? 0 : degree;
		Sleep(1);
	}
	dcMem.DeleteDC();
	dcBkgd.DeleteDC();
	bmpBkgd.DeleteObject();
	EnumChildWindows(pMainDlg->GetSafeHwnd(), EnumChildProc, 1L);

//	CLEAN_CODE
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值