chenyonken
弹指间
采纳率51.2%
2017-08-16 03:39

MFC使用CMemory封装类来实现透明位图出错

5

问题描述:
想要实现的效果是在屏幕出现一只飞舞的蝴蝶(带有透明色背景位图),使用了CMemory封装类
来实现,虽然可以使蝴蝶飞动,但最终蝴蝶图片的背景还是没有去除掉,

Memory.h头文件

 #pragma once
#include "afxwin.h"
#ifndef __MEMDC_H__
#define __MEMDC_H__


class CMemoryDC :public CDC
{
    CSize m_size;
public:
    void BitTrans(
        int nXDest,     // 目标起点X
        int nYDest,     // 目标起点Y
        int nWidthDest, // 目标宽度
        int nHeightDest,// 目标高度
        CDC* pDC,       // 目标DC
        int nXSrc,      // 来源起点X
        int nYSrc,      // 来源起点Y
        COLORREF crTrans// 透明色
    )
    {
        CMemoryDC 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    // 透明色
    )
    {
        CMemoryDC 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);
    }
    CMemoryDC()
    {
        m_size.cx = m_size.cy = 0;
    }
    //从资源中加载位图
    BOOL LoadBitmap(UINT nBitmapID, CDC* pDC = NULL)
    {
        CBitmap bitmap;
        bitmap.LoadBitmap(nBitmapID);
        BITMAP bm;
        bitmap.GetBitmap(&bm);
        m_size.cx = bm.bmWidth;
        m_size.cy = bm.bmHeight;
        CreateCompatibleDC(pDC);
        SelectObject(bitmap);
        return TRUE;
    }
    CMemoryDC(UINT nBitmapID, CDC* pDC = NULL)
    {
        LoadBitmap(nBitmapID, pDC);
    }
    //从.bmp文件中加载位图
    BOOL LoadBitmap(LPCTSTR szBitmapFile, CDC* pDC = NULL)
    {
        HBITMAP hBitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(),
            szBitmapFile, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
        BITMAP bm;
        GetObject(hBitmap, sizeof(bm), &bm);
        m_size.cx = bm.bmWidth;
        m_size.cy = bm.bmHeight;
        CreateCompatibleDC(pDC);
        SelectObject(hBitmap);
        return TRUE;
    }
    CMemoryDC(LPCTSTR szBitmapFile, CDC* pDC = NULL)
    {
        LoadBitmap(szBitmapFile, pDC);
    }
    //创建一张空白内存画布
    BOOL Create(int cx, int cy, CDC* pDC = NULL)
    {
        CreateCompatibleDC(pDC);
        CBitmap bitmap;
        if (pDC)
            bitmap.CreateCompatibleBitmap(pDC, cx, cy);
        else
            bitmap.CreateCompatibleBitmap(&CClientDC(NULL), cx, cy);
        m_size.cx = cx;
        m_size.cy = cy;
        SelectObject(bitmap);
        return TRUE;
    }
    CMemoryDC(int cx, int cy, CDC* pDC = NULL)
    {
        Create(cx, cy, pDC);
    }
    //摧毁
    BOOL DeleteDC()
    {
        if (!GetSafeHdc())
            return TRUE;
        CBitmap * pBitmap = GetCurrentBitmap();
        pBitmap->DeleteObject();
        return CDC::DeleteDC();
    }
    ~CMemoryDC()
    {
        DeleteDC();
    }
    inline int Width() { return m_size.cx; }
    inline int Height() { return m_size.cy; }
    void CMemoryDC::BitRgn(CRgn &rgn,/*目标区域*/COLORREF crTrans/*透明色*/)
    {
        //根据当前位图和透明色生成一个不规则区域
        int i = 0, j = 0;
        rgn.CreateRectRgn(0, 0, 0, 0);
        while (i < m_size.cx)
        {
            j = 0;
            while (j < m_size.cy)
            {
                if (GetPixel(i, j) - crTrans)
                {
                    //如果不是透明色就在区域内增加一个点
                    CRgn r;
                    r.CreateRectRgn(i, j, i + 1, j + 1);
                    rgn.CombineRgn(&rgn, &r, RGN_OR);
                }
                ++j;
            }
            ++i;
        }
    }
};

\#endif //__MEMDC_H__

主对话框头文件:FlyRgnDlg.h


// FlyRgnDlg.h : header file
//

#pragma once

#include "MemDC.h"
// CFlyRgnDlg dialog
class CFlyRgnDlg : public CDialogEx
{
// Construction
public:
    enum { FLY_CNT = 7 };
    CMemoryDC m_dc[FLY_CNT];
    CRgn m_rgn[FLY_CNT];
    int m_nIndex;   //当前帧数
    CPoint m_pos;   //当前位置

public:
    CFlyRgnDlg(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
#ifdef AFX_DESIGN_TIME
    enum { IDD = IDD_FLYRGN_DIALOG };
#endif

    protected:
    virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support


// Implementation
protected:
    HICON m_hIcon;

    // Generated message map functions
    virtual BOOL OnInitDialog();
    afx_msg void OnPaint();
    afx_msg HCURSOR OnQueryDragIcon();
    DECLARE_MESSAGE_MAP()
public:
    afx_msg void OnTimer(UINT_PTR nIDEvent);
    void OnDraw(CDC* pDC);
    afx_msg BOOL OnEraseBkgnd(CDC* pDC);
    afx_msg void OnNcPaint();
};

主对话框cpp文件:FlyRgnDlg.cpp


// FlyRgnDlg.cpp : implementation file
//

#include "stdafx.h"
#include "FlyRgn.h"
#include "FlyRgnDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CFlyRgnDlg dialog


#define TRANSCOLOR RGB(0,0,0)

CFlyRgnDlg::CFlyRgnDlg(CWnd* pParent /*=NULL*/)
    : CDialogEx(IDD_FLYRGN_DIALOG, pParent)
{
    m_nIndex = 0;

    m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CFlyRgnDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CFlyRgnDlg, CDialogEx)
    ON_WM_PAINT()
    ON_WM_QUERYDRAGICON()
    ON_WM_TIMER()
    ON_WM_ERASEBKGND()
    ON_WM_NCPAINT()
END_MESSAGE_MAP()


// CFlyRgnDlg message handlers

BOOL CFlyRgnDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();
    int i = 0;
    CString str;
    while (i < FLY_CNT)
    {
        //加载多张动画图片并且每张图片根据透明色区域生成一个区域
        str.Format(_TEXT("./flys/%03d.bmp"), i + 1);
        m_dc[i].LoadBitmapW(str);
        m_dc[i].BitRgn(m_rgn[i], TRANSCOLOR);
        i++;
    }
    ModifyStyle(GetStyle(), 0);//去掉标题栏和边框
    ModifyStyleEx(GetExStyle(), WS_EX_TOOLWINDOW);//不在任务栏显示

                                                  //创建一个屏幕随机坐标
    int cx = GetSystemMetrics(SM_CXSCREEN);
    int cy = GetSystemMetrics(SM_CYSCREEN);
    srand(time(NULL));
    m_pos.x = rand() % (cx - m_dc[0].Width());
    m_pos.y = rand() % (cy - m_dc[0].Height());


    CMemoryDC &mdc = m_dc[m_nIndex];//设置窗口位置并置顶
    SetWindowPos(&wndTopMost, m_pos.x, m_pos.y, mdc.Width(), mdc.Height(), 0);
    CRgn rgn;   //选择一个帧区域复制后设置到窗口
    rgn.CreateRectRgn(0, 0, 0, 0);
    rgn.CopyRgn(&m_rgn[m_nIndex]);
    SetWindowRgn(rgn, TRUE);
    //启动定时器
    SetTimer(0, 64, NULL);
    // 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

    // TODO: Add extra initialization here

    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 CFlyRgnDlg::OnPaint()
{
    CPaintDC dc(this);
    OnDraw(&dc);
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CFlyRgnDlg::OnQueryDragIcon()
{
    return static_cast<HCURSOR>(m_hIcon);
}



void CFlyRgnDlg::OnTimer(UINT_PTR nIDEvent)
{
    // TODO: Add your message handler code here and/or call default
/*  CmemoryDC &mdc = m_dc[m_nIndex];
    MoveWindow(m_pos.x, m_pos.y, mdc.GetWidth(), mdc.GetHeight());
    CRgn rgn;
    rgn.CreateRectRgn(0, 0, 0, 0);
    rgn.CopyRgn(&m_rgn[m_nIndex]);
    SetWindowRgn(rgn, TRUE);
    CClientDC dc(this);
    mdc.BitTrans(0, 0, mdc.GetWidth(), mdc.GetHeight(),
        &dc, 0, 0, SRCCOPY);
    static int cx = 5, cy = 5;
    m_pos.Offset(cx, cy);
    if (m_pos.x + mdc.GetWidth() > GetSystemMetrics(SM_CXSCREEN) || m_pos.x < 0)
        cx *= -1;
    if (m_pos.y + mdc.GetHeight() > GetSystemMetrics(SM_CYSCREEN) || m_pos.y < 0)
        cy *= -1;
    if (++m_nIndex >= FLY_CNT)
        m_nIndex = 0;*/

    CClientDC dc(this);
    Invalidate(FALSE);
    CDialogEx::OnTimer(nIDEvent);
}


void CFlyRgnDlg::OnDraw(CDC *pDC)
{
    CMemoryDC &mdc = m_dc[m_nIndex];
    MoveWindow(m_pos.x, m_pos.y, mdc.Width(), mdc.Height());
    CRgn rgn;
    rgn.CreateRectRgn(0, 0, 0, 0);
    rgn.CopyRgn(&m_rgn[m_nIndex]);
    SetWindowRgn(rgn, TRUE);
    mdc.BitTrans(0, 0, mdc.Width(), mdc.Height(),
    pDC, 0, 0, TRANSCOLOR);
    static int cx = 5, cy = 5;
    m_pos.Offset(cx, cy);
    if (m_pos.x + mdc.Width() > GetSystemMetrics(SM_CXSCREEN) || m_pos.x < 0)
    cx *= -1;
    if (m_pos.y + mdc.Height() > GetSystemMetrics(SM_CYSCREEN) || m_pos.y < 0)
    cy *= -1;
    if (++m_nIndex >= FLY_CNT)
    m_nIndex = 0;


}


BOOL CFlyRgnDlg::OnEraseBkgnd(CDC* pDC)
{
    // TODO: Add your message handler code here and/or call default
    return TRUE;
//  return CDialogEx::OnEraseBkgnd(pDC);
}




void CFlyRgnDlg::OnNcPaint()
{
    // TODO: Add your message handler code here
    // Do not call CDialogEx::OnNcPaint() for painting messages
}

失败的效果

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • shen_wei shen_wei 4年前
  • qq_37116094 qq_37116094 4年前

    可以试试用2张背景图

    点赞 评论 复制链接分享