轉載請注明出處:http://www.cnblogs.com/Ray1024
一、引言
最近在使用Direct2D進行繪制工作中,需要實現使用Direct2D繪制網格的功能。在網上查了很多資料,終於實現了,把方法貼到這里供大家參考。
二、繪制網格
2.1 API接口
首先介紹一下繪制網格中使用到的重要接口ID2D1BitmapRenderTarget,它繼承自ID2D1RenderTarget,會寫入到中間紋理。對於創建與 ID2D1BitmapBrush 結合使用的圖案,或緩存要反復使用的繪制數據,這十分有用。它僅僅比基類多了一個函數GetBitmap,此函數可以將內部的繪制數據輸出到位圖ID2D1Bitmap中,如下:
語法: virtual HRESULT GetBitmap([out] ID2D1Bitmap **bitmap) = 0; 功能: 檢索此呈現器目標的位圖。返回的位圖可用於繪制操作。 參數: bitmap 此方法返回時,包含指向此呈現器目標的位圖的指針地址。此位圖可用於繪制操作。 返回值:HRESULT 如果該方法成功,則返回 S_OK。 否則,將返回錯誤代碼。HRESULT.
創建ID2D1BitmapRenderTarget對象的函數為ID2D1RenderTarget::CreateCompatibleRenderTarget,這個函數有6個重載,我們只介紹其中一個,有興趣的朋友可以查看msdn文檔,介紹如下:
語法:HRESULT CreateCompatibleRenderTarget(D2D1_SIZE_F desiredSize,[out] ID2D1BitmapRenderTarget **bitmapRenderTarget); 功能:創建新位圖呈現器目標,以供在中間屏幕外繪制期間使用。新位圖呈現器目標與當前呈現器目標兼容,並且與當前呈現器目標有相同的像素格式。 參數: desiredSize 以與設備無關的像素表示的新呈現器目標的所需大小。 bitmapRenderTarget 此方法返回時將包含一個指針的地址,該指針指向一個新位圖呈器現目標。此參數以未初始化的狀態傳遞。 返回值:HRESULT 如果該方法成功,則返回 S_OK。 否則,將返回錯誤代碼。HRESULT.
我們還要用到位圖畫刷ID2D1BitmapBrush,這個接口不用特殊介紹,我們介紹一下創建畫刷時需要的一個結構體,這個結構體用來描述 ID2D1BitmapBrush 的擴展模式和內插模式:
struct D2D1_BITMAP_BRUSH_PROPERTIES { D2D1_EXTEND_MODE extendModeX; //一個值,用來描述畫筆對超過其位圖范圍的區域進行水平平鋪的方式。 D2D1_EXTEND_MODE extendModeY; //一個值,用來描述畫筆對超過其位圖范圍的區域進行垂直平鋪的方式。 D2D1_BITMAP_INTERPOLATION_MODE interpolationMode; //一個值,用來指定對位圖進行縮放或旋轉時使用的內插方式。 };
這個結構體前兩個成員的類型都是枚舉類型D2D1_EXTEND_MODE,它指定畫筆如何在其常規內容區域之外的區域進行繪制。如下:
typedef enum { D2D1_EXTEND_MODE_CLAMP = 0,//在常規內容區域以外的所有區域重復畫筆內容邊上的像素。 D2D1_EXTEND_MODE_WRAP = 1,//重復畫筆的內容。 D2D1_EXTEND_MODE_MIRROR = 2 //與 D2D1_EXTEND_MODE_WRAP 相同,但畫筆的內容將翻轉顯示。(畫筆的常規內容在繪制時不會進行轉換。) } D2D1_EXTEND_MODE;
還有一個枚舉類型D2D1_BITMAP_INTERPOLATION_MODE,用來指定縮放或旋轉圖像時所使用的算法。如下:
typedef enum { D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR = 0,//使用離當前呈現像素最近的位圖像素的精確顏色。 D2D1_BITMAP_INTERPOLATION_MODE_LINEAR = 1 //從離當前呈現像素最近的四個位圖像素來內插顏色。 } D2D1_BITMAP_INTERPOLATION_MODE;
要拉伸圖像,原始圖像中的每個像素都必須映射到較大的圖像中的一組像素。要壓縮圖像,原始圖像中的一組像素必須映射到較小的圖像中的單個像素。我們這里用不到這些,就不過多介紹了。
到這里我們所需要的API接口就介紹完了。
2.2 思路介紹
介紹完需要的API接口之后,我們來看一下實現網格繪制的思路:
a.創建一個網格粒度大小的ID2D1BitmapRenderTarget;
b.在ID2D1BitmapRenderTarget上繪制兩條直線,分別在ID2D1BitmapRenderTarget的左邊和上邊;
c.從ID2D1BitmapRenderTarget創建位圖;
d.指定畫刷的的屬性,讓它對超過位圖畫刷范圍外的區域進行重復繪制。
也就是說在一個位圖畫刷上保存一個網格,並指定畫刷繪制的時候對范圍外的區域進行重復繪制,如下圖所示:
2.3 代碼實現
這是繪制網格的代碼部分:
// 網格粒度 float meshLength = 20.f; // 創建bitmapRT if (SUCCEEDED(hr)) { hr = m_pRT->CreateCompatibleRenderTarget( D2D1::SizeF(meshLength,meshLength), &m_pBitmapRT); } // 創建bitmapBrush if (SUCCEEDED(hr)) { m_pBitmapRT->BeginDraw(); m_pBitmapRT->DrawLine(D2D1::Point2F(0,0),D2D1::Point2F(meshLength,0),m_pBrush); m_pBitmapRT->DrawLine(D2D1::Point2F(0,0),D2D1::Point2F(0,meshLength),m_pBrush); m_pBitmapRT->EndDraw(); m_pBitmapRT->GetBitmap(&m_pBitmap); D2D1_BITMAP_BRUSH_PROPERTIES bbp; bbp.extendModeX = D2D1_EXTEND_MODE_WRAP; bbp.extendModeY = D2D1_EXTEND_MODE_WRAP; bbp.interpolationMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR; m_pRT->CreateBitmapBrush(m_pBitmap, bbp, &m_pBitmapBrush); }
上面的代碼中先創建了bitmapRT,然后在bitmapRT上繪制一個網格的兩個邊,再從bitmapRT上獲取位圖,根據位圖創建位圖畫刷。
下面是繪制部分的代碼:
RECT clientRect; GetClientRect(m_hwnd, &clientRect); D2D1_RECT_F rc = D2D1::RectF(clientRect.left,clientRect.top,clientRect.right,clientRect.bottom); // 開始繪制 m_pRT->BeginDraw(); m_pRT->SetTransform(D2D1::Matrix3x2F::Identity()); m_pRT->Clear(D2D1::ColorF(D2D1::ColorF::Black)); // 繪制 m_pRT->FillRectangle( rc, m_pBitmapBrush); // 結束繪制 hr = m_pRT->EndDraw();
繪制的演示效果如下圖:
在這里完整代碼代碼就不貼出了,有興趣的朋友可以點擊此處下載Demo源碼,Demo源碼是Direct2DTests目錄下的D2DMesh文件。
三、結語
這樣我們就成功地利用Direct2D繪制出了網格,希望可以幫到大家。