[MFC]SDI在圖片背景上實現文本跟隨鼠標移動

SDI是單文檔接口應用程序的簡稱。本文要實現的是在視圖區域顯示一張圖片,而後在圖片表層顯示文字,而且文字跟隨鼠標移動。思考一下,能夠判斷這個問題一共分爲如下幾個部分:一、顯示圖片;二、找到鼠標的位置;三、插入文字;四、自動移動文字。如下分步驟說明。函數

一、首先是使用「打開」方式打開一張圖片並顯示,出於方便這裏僅僅針對bmp格式的圖片,具體是在CYourView類內部的OnDraw函數內添加以下代碼:this

    if(GetDocument()->GetPathName() != "")//載入圖片
    {
        HBITMAP bitmap;
        bitmap = (HBITMAP)LoadImage(AfxGetInstanceHandle(),
            GetDocument()->GetPathName(), IMAGE_BITMAP, 0, 0,
            LR_LOADFROMFILE|LR_CREATEDIBSECTION);
        HBITMAP OldBitmap;
        CBitmap cbitmap;
        cbitmap.Attach(bitmap);
        cbitmap.GetBitmap(&m_bitmap);


        CDC MemDC;
        MemDC.CreateCompatibleDC(pDC);
        CRect rect;
        GetClientRect(&rect);
        OldBitmap = (HBITMAP)MemDC.SelectObject(bitmap);
        pDC->BitBlt(0, 0, m_bitmap.bmWidth, m_bitmap.bmHeight, &MemDC, 0, 0, SRCCOPY);
        MemDC.SelectObject(OldBitmap);
        MemDC.DeleteDC();
    }

其中m_bitmap是一個BITMAP對象。顯示圖片的方法是比較常見的,這裏再也不說明。spa

二、接下來須要知道有個消息處理函數OnMouseMove用來處理鼠標移動消息。因此首先要添加這個函數,這個能夠在Class Wizard中找到。先添加這個函數。code

OnMouseMove(UINT nFlags, CPoint point);

這個函數的第二個參數CPoint對象就是鼠標所在位置,可使用以下方法找到對應座標:orm

Xcoords = point.x;
Ycoords = point.y;

 

三、而後就要插入文字了,在SDI中特定位置插入文字其實很簡單,使用CDC->DrawText方法就能夠實現。這個函數能夠實如今指定的矩形框內添加格式化文本,有點像word中的插入對話框。具體語法以下:對象

int DrawText(const CString& str, LPRECT lpRect, UINT nFormat);

第一個參數是要輸出的CString對象,第二個參數是顯示文本的矩形區域,最後一個參數是文本的格式,好比居左居中等,一個使用例子以下:blog

CClientDC cdc(this);
cdc.DrawText("text", CRect(left, top, reight, bottom), DT_LEFT);


 

四、插入文字實現以後,就要進行文字的移動了。首先我想到的是SDI中圖形的移動。好比畫一個矩形CDC->Rectangle方法,而後移動這個矩形框。有一個經常使用的圖形移動方式是進行異或畫圖,即便用CDC->SetROP2(R2_XORPEN)這個方法畫圖。使用與原來畫筆異或屏幕顏色再畫一遍,能夠實現清除已畫圖形的效果,所以圖形的移動也就是以下過程:接口

(1)異或畫圖圖片

(2)在原位置再次異或畫圖內存

(3)在新位置異或畫圖

(4)...

把畫圖與OnMouseMove結合就能容易實現圖形跟隨鼠標移動。可是一樣的方法能夠用於文本的移動嗎?答案是否認的。由於DrawText與其餘畫圖函數有本質的區別,由於其沒有用到畫筆,因此SetROP2這個方法整個對文本沒有效果。不過,有了移動圖形的方法,能夠容易想到所謂移動(圖形或者文本)的本質其實就是首先把原位置的內容清除,而後在新位置從新畫(好像是廢話)。

  所以,移動文字的關鍵就在於如何把原位置的文字清除。(用DrawText插入一個空文本的方法實行不一樣的,由於不會影響已經插入的文本。)一個容易想到的方法是將原位置(DrawText對應的矩形)整個複製下來,而後在插入文字後從新繪製上去,就用覆蓋的方式清除了文字。因而有了以下的方法。

首先添加一個截圖函數,叫作ScreenShot。

CBitmap* CYourView::ScreenShot(CDC* pDC, CBitmap *memBitmap, int Xcoords, int Ycoords, int Width, int Height)
{
    CDC memDC;
    memDC.CreateCompatibleDC(pDC);
    
    memBitmap->CreateCompatibleBitmap(pDC, Width, Height);
    memDC.SelectObject(memBitmap);
    memDC.BitBlt(0, 0, Width, Height, pDC, Xcoords, Ycoords, SRCCOPY);  
    memDC.DeleteDC();

    return memBitmap;

}

這個函數的處理過程很簡單,將指定位置和大小的矩形內部的圖片內容保存起來。

具體的實現文本跟隨鼠標移動的代碼在OnMouseMove函數中:

void CYourView::OnMouseMove(UINT nFlags, CPoint point)
{
    // TODO: 在此添加消息處理程序代碼和/或調用默認值
    CClientDC cdc(this);
    Xcoords = point.x;
    Ycoords = point.y;
if(m_loaded_flag) 
    {
        if(m_first_show_flag == false)
        {
        
            OldX = Xcoords+10;
            OldY = Ycoords-10;
            memBitmap.DeleteObject();
            memBmp = ScreenShot(&cdc, &memBitmap, OldX, OldY, 100, 50);

            cdc.SetBkMode(TRANSPARENT);
            cdc.DrawText("text",CRect(OldX,OldY, OldX+100, OldY+50),DT_LEFT);
            m_first_show_flag = true;
        }
        else
        {
            cdc.SetBkMode(TRANSPARENT);//去除原來位置的文字
            BITMAP bm;
            memBmp->GetObject (sizeof(BITMAP),&bm);
            CDC MemDC;
            MemDC.CreateCompatibleDC(&cdc);
            CBitmap *pOldBitmap=MemDC.SelectObject(memBmp); 
            cdc.BitBlt(OldX,OldY,bm.bmWidth ,bm.bmHeight,&MemDC,0,0,SRCCOPY);    
            MemDC.SelectObject (pOldBitmap);
            MemDC.DeleteDC();
            
            //新座標做爲下次使用的前一次座標
            OldX = Xcoords+10;
            OldY = Ycoords-10;
            memBitmap.DeleteObject();
            memBmp = ScreenShot(&cdc, &memBitmap, OldX, OldY, 100, 50);
            cdc.DrawText("text",CRect(OldX, OldY, OldX+100, OldY+50),DT_LEFT);//在新位置添加文字
            
        }

    }

    CScrollView::OnMouseMove(nFlags, point);
}

因爲第一次鼠標放上去的狀態跟後面的移動狀態有點區別,說以代碼中分開處理,所以還須要在OnDraw函數中if(GetDocument()->GetPathName() != "")語句內添加以下內容:

        m_loaded_flag = true;
     if(m_first_show_flag == true)
        {
            Invalidate();
            m_first_show_flag = false;
        }

當載入圖片後才顯示跟隨鼠標的文字。而且當移動圖片時刷新以清除文字。

總的來講整個過程是比較簡單的,曾經看到過一個採用OnTimer實現的文字跟隨鼠標移動的例子,其實就是不斷地刷新屏幕來顯示不一樣的文字,當有背景圖時會出現嚴重的閃爍,須要採用其餘方法來避免閃爍,可是使用個人方法就沒有這個問題。一小部分的重繪在視覺上是沒有影響的。

另外要注意使用到CDC對象的回收,若是使用CDC *pDC = this->DetDC()這樣的方法建立一個設備上下文則須要在不用的時候使用pDC->DeleteDC()刪除DC,否則就會致使嚴重的內存泄露。可是使用CClientDC cdc(this)建立的CDC對象系統可以自動回收,因此以上代碼中採用的是這種方法。

相關文章
相關標籤/搜索