最终实现效果:
过程:
一:从网上搜索一套带透明背景的位图(要做动画帧用),或者使用附件里的Fly紫文件夹里的透明位图,如果需要更换位图,需要修改相关代码
二:使用vc6.0新建一个基于对话框的MFC程序,直接打开资源文件中的对话框界面,将对话框BORDER属性设为NONE
三:将已经封装好的MemDC.h(MemdDC.h要想在vs2016中使用,需要更改里面的类名,会与MFC的类名冲突,其他版本不清楚)文件导入工程,添加相关头文件,主要使用到MemDC里的BitTrans()函数和LoadBitmap()函数,前者用于将不透明位图输出为透明位图,后者用来加载位图
四:修改对话框头文件和CPP文件
MemDC.h
#ifndef __MEM_DC__H__
#define __MEM_DC__H__
//防止重复编译
class CMemDC : public CDC
{
CSize m_size;
public:
~CMemDC()
{
DeleteDC();
}
CMemDC()
{
m_size.cx = m_size.cy=0;
}
CMemDC(UINT nBitmap,CDC* pDC=NULL)
{
LoadBitmap(nBitmap,pDC);
}
CMemDC(LPCSTR szFile,CDC* pDC=NULL)
{
LoadBitmap(szFile,pDC);
}
CMemDC(int cx,int cy,CDC* pDC=NULL)
{
Create(cx,cy,pDC);
}
BOOL DeleteDC()
{
if(!GetSafeHdc())
return FALSE;
CBitmap* pBitmap = GetCurrentBitmap();
if(pBitmap)
pBitmap ->DeleteObject();
return CDC::DeleteDC();
}
int GetWidth(){return m_size.cx;}
int GetHeight(){return m_size.cy;}
BOOL Create(int cx,int cy,CDC* pDC=NULL)
{//创建空白位图
CBitmap bmp;
if(!bmp.CreateCompatibleBitmap(pDC,cx,cy))
return FALSE;
m_size.cx = cx;
m_size.cy = cy;
CreateCompatibleDC(pDC);
SelectObject(&bmp);
return TRUE;
}
BOOL LoadBitmap(UINT nBitmap,CDC* pDC=NULL)
{//进程内位图资源加载
CBitmap bmp;
if(!bmp.LoadBitmap(nBitmap))
return FALSE;
BITMAP bm;
bmp.GetBitmap(&bm);
m_size.cx = bm.bmWidth;
m_size.cy = bm.bmHeight;
CreateCompatibleDC(pDC);
SelectObject(&bmp);
return TRUE;
}
BOOL LoadBitmap(LPCSTR szFile,CDC* pDC=NULL)
{//进程外位图图片文件加载
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,szFile,IMAGE_BITMAP,
0,0,LR_LOADFROMFILE);
if(!hBitmap)
return FALSE;
BITMAP bm;
GetObject(hBitmap,sizeof(bm),&bm);
m_size.cx = bm.bmWidth;
m_size.cy = bm.bmHeight;
CreateCompatibleDC(pDC);
SelectObject(hBitmap);
return TRUE;
}
void BitTrans(
int nXDest, // 目标起点X
int nYDest, // 目标起点Y
int nWidthDest, // 目标宽度
int nHeightDest,// 目标高度
CDC* pDC, // 目标DC
int nXSrc, // 来源起点X
int nYSrc, // 来源起点Y
COLORREF crTrans// 透明色
)
{
CMemDC dcImage(nWidthDest, nHeightDest,pDC);//临时DC
CBitmap bmpMask;
bmpMask.CreateBitmap(nWidthDest, nHeightDest, 1, 1, NULL);
// 创建单色掩码位图
CDC dcMask;//掩码DC
dcMask.CreateCompatibleDC(pDC);
dcMask.SelectObject(bmpMask);
//将载入位图的内存DC中的位图,拷贝到临时DC中
dcImage.BitBlt( 0, 0, nWidthDest, nHeightDest, this, nXSrc, nYSrc, SRCCOPY);
// 设置临时DC的透明色
dcImage.SetBkColor(crTrans);
//掩码DC的透明区域为白色其它区域为黑色
dcMask.BitBlt(0, 0, nWidthDest, nHeightDest, &dcImage, 0, 0, SRCCOPY);
//临时DC透明区域为黑色,其它区域保持不变
dcImage.SetBkColor(RGB(0,0,0));
dcImage.SetTextColor(RGB(255,255,255));
dcImage.BitBlt( 0, 0, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);
// 目标DC透明部分保持屏幕不变,其它部分变成黑色
pDC ->SetBkColor(RGB(255,255,255));
pDC ->SetTextColor(RGB(0,0,0));
pDC ->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);
pDC ->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcImage, 0, 0, SRCPAINT);
}
void StretchTrans(
int nXDest, // 目标起点X
int nYDest, // 目标起点Y
int nWidthDest, // 目标宽度
int nHeightDest, // 目标高度
CDC* pDC, // 目标DC
int nXSrc, // 来源起点X
int nYSrc, // 来源起点Y
int nWidthSrc, // 来源宽度
int nHeightSrc, // 来源高度
COLORREF crTrans // 透明色
)
{
CMemDC dcImage(nWidthDest, nHeightDest,pDC);//临时DC
CBitmap bmpMask;
// 创建单色掩码位图
bmpMask.CreateBitmap(nWidthDest, nHeightDest, 1, 1, NULL);
CDC dcMask;
dcMask.CreateCompatibleDC(pDC);
dcMask.SelectObject(bmpMask);
// 将载入位图的内存DC中的位图,拷贝到临时DC中
if (nWidthDest == nWidthSrc && nHeightDest == nHeightSrc)
dcImage.BitBlt(0, 0, nWidthDest, nHeightDest, this, nXSrc, nYSrc, SRCCOPY);
else
dcImage.StretchBlt(0, 0, nWidthDest, nHeightDest,
this, nXSrc, nYSrc, nWidthSrc, nHeightSrc, SRCCOPY);
// 设置临时DC的透明色
dcImage.SetBkColor( crTrans);
//掩码DC的透明区域为白色其它区域为黑色
dcMask.BitBlt(0,0,nWidthDest, nHeightDest,&dcImage,0,0,SRCCOPY);
//临时DC透明区域为黑色,其它区域保持不变
dcImage.SetBkColor(RGB(0,0,0));
dcImage.SetTextColor(RGB(255,255,255));
dcImage.BitBlt(0, 0, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);
// 目标DC透明部分保持屏幕不变,其它部分变成黑色
pDC ->SetBkColor(RGB(255,255,255));
pDC ->SetTextColor(RGB(0,0,0));
pDC ->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcMask, 0, 0, SRCAND);
pDC ->BitBlt(nXDest, nYDest, nWidthDest, nHeightDest, &dcImage, 0, 0, SRCPAINT);
}
};
#endif
主对话框头文件FlyDlg.h实现
// FlyDlg.h : header file
//
#if !defined(AFX_FLYDLG_H__B92F35C6_0BDF_4DB7_BDDE_ED28D1CBBEF4__INCLUDED_)
#define AFX_FLYDLG_H__B92F35C6_0BDF_4DB7_BDDE_ED28D1CBBEF4__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CFlyDlg dialog
#include "MemDC.h"
class CFlyDlg : public CDialog
{
enum{COUNT=7};
CMemDC m_dcBack;//
CMemDC m_dc[COUNT];
int m_nIndex;
CPoint m_pos;
public:
void OnDraw(CDC* pDC);
CFlyDlg(CWnd* pParent = NULL); // standard constructor
// Dialog Data
//{{AFX_DATA(CFlyDlg)
enum { IDD = IDD_FLY_DIALOG };
// NOTE: the ClassWizard will add data members here
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CFlyDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
// Generated message map functions
//{{AFX_MSG(CFlyDlg)
virtual BOOL OnInitDialog();
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnTimer(UINT nIDEvent);
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
afx_msg void OnNcPaint();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_FLYDLG_H__B92F35C6_0BDF_4DB7_BDDE_ED28D1CBBEF4__INCLUDED_)
主对话框CPP文件实现FlyDlg.cpp
// FlyDlg.cpp : implementation file
//
#include "stdafx.h"
#include "Fly.h"
#include "FlyDlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CFlyDlg dialog
#define TRANSCOLOR RGB(0,0,255)//透明色为紫色
CFlyDlg::CFlyDlg(CWnd* pParent /*=NULL*/)
: CDialog(CFlyDlg::IDD, pParent)
{
m_nIndex=0;
m_pos.x = m_pos.y=0;
//{{AFX_DATA_INIT(CFlyDlg)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CFlyDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CFlyDlg)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CFlyDlg, CDialog)
//{{AFX_MSG_MAP(CFlyDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_WM_TIMER()
ON_WM_ERASEBKGND()
ON_WM_NCPAINT()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CFlyDlg message handlers
BOOL CFlyDlg::OnInitDialog()
{
CDialog::OnInitDialog();
m_dcBack.LoadBitmap("./Flys_紫/Back.bmp");
int i=0;
CString str;
while(i<COUNT)
{
str.Format("./Flys_紫/%03d.bmp",i+1);
m_dc[i].LoadBitmap(str);
++i;
}
SetTimer(1,64,NULL);
MoveWindow(0,0,m_dcBack.GetWidth(),m_dcBack.GetHeight());
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
return TRUE; // return TRUE unless you set the focus to a control
}
// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.
void CFlyDlg::OnPaint()
{
CPaintDC dc(this);
OnDraw(&dc);
}
// The system calls this to obtain the cursor to display while the user drags
// the minimized window.http://www.fodizi.com/fofa/list/248.htm
HCURSOR CFlyDlg::OnQueryDragIcon()
{
return (HCURSOR) m_hIcon;
}
//为什么画面上方会产生秘密文字???
void CFlyDlg::OnTimer(UINT nIDEvent)
{
//如果使用注释掉的这一部分,则是没有采用双缓冲,需要注释掉OnPaint函数里的OnDraw()函数和这里的Invalideate()函数
//如果使用OnDraw函数,则是采用了双缓冲技术,OnDraw()在OnPaint()中调用,所以要使用Invalidate()函数来刷新界面
/*
static int i=0;
CClientDC dc(this);
//显示背景
dc.BitBlt(0,0,m_dcBack.GetWidth(),m_dcBack.GetHeight(),
&m_dcBack,0,0,SRCCOPY);
static int cx=m_dc[m_nIndex].GetWidth();//离开函数数值不丢失
static int cy=m_dc[m_nIndex].GetHeight();
static dx=5,dy=5;//运动方向
//显示蝴蝶
m_dc[m_nIndex].BitTrans(m_pos.x,m_pos.y,cx,cy,&dc,0,0,TRANSCOLOR);
//控制蝴蝶位置
m_pos.Offset(dx,dy);
if(m_pos.x + cx> m_dcBack.GetWidth() || m_pos.x <0)
dx*=-1;
if(m_pos.y + cy> m_dcBack.GetHeight() || m_pos.y < 0)
dy *=-1;
if(++m_nIndex>=COUNT)
m_nIndex=0;
*/
Invalidate(FALSE);
}
//使用双缓冲技术
void CFlyDlg::OnDraw(CDC *pDC)
{
CRect rect;
GetClientRect(rect);
CMemDC mdc(rect.Width(),rect.Height(),pDC);
//如果不用背景图去覆盖掉上一次的蝴蝶,可以用FillSolidRect这一句去覆盖(必须要有东西来覆盖掉上一次的蝴蝶),
//但如果在用绿色覆盖后又再用背景图覆盖,如果没有使用双缓冲技术,会发生闪烁
// mdc.FillSolidRect(0,0,rect.right,rect.Height(),RGB(255,0,0));
//既可以拉伸,也可以直接输入到缓冲内存中
//如果TRANSCOLOR为黑色并且把FillSolidRect()的注释去掉,会有神秘文字出现,改成其他颜色不会,这种现象与图本身有关
// m_dcBack.StretchTrans(0,0,rect.right,rect.bottom,&mdc,0,0,m_dcBack.GetWidth(),
// m_dcBack.GetHeight(),TRANSCOLOR);
m_dcBack.BitTrans(0,0,rect.right,rect.bottom,&mdc,0,0,RGB(0,0,0));
// dc.BitBlt(0,0,m_dcBack.GetWidth(),m_dcBack.GetHeight(),&m_dcBack,0,0,SRCCOPY);
static int cx = m_dc[m_nIndex].GetWidth();//static变量第一次进入函数式初始化
static int cy = m_dc[m_nIndex].GetHeight();//离开函数数值不丢失
static dx=5,dy=5;//运动方向
m_dc[m_nIndex].BitTrans(m_pos.x,m_pos.y,cx,cy,&mdc,0,0,TRANSCOLOR);
pDC ->BitBlt(0,0,mdc.GetWidth(),mdc.GetHeight(),&mdc,0,0,SRCCOPY);
m_pos.Offset(dx,dy);
if(m_pos.x + cx> rect.right || m_pos.x <0)
dx*=-1;
if(m_pos.y + cy> rect.bottom || m_pos.y < 0)
dy *=-1;
if(++m_nIndex>=COUNT)
m_nIndex=0;
}
BOOL CFlyDlg::OnEraseBkgnd(CDC* pDC)
{
return TRUE;
}
void CFlyDlg::OnNcPaint()
{
}
程序遗留问题:
如果带有透明背景的位图的透明色为黑色的话,在运行的时候蝴蝶图片的背景仍然不会被去除掉,原因暂时未弄明白,如果有网友看了这份代码,并且
明白了这个问题的原因的话,如果能在评论里或写信指导一下本人,本人将感激不尽!!
附件下载:
=工程文件
参考: