Direct2D教程(九)渲染位圖


概述

這篇的標題更確切的說應該叫位圖畫刷,這樣才好和前幾篇對應起來。在Direct2D中,位圖的渲染也是通過畫刷來實現的。

Direct2D中並沒有直接操作位圖的接口,而是借助WIC(Windows Image Component)來完成的。今天我們來看看如何在Direct2D中加載並顯示位圖。這個方法可以用來渲染背景。基本步驟如下。

  • 從文件創建WIC位圖
  • 由WIC位圖創建D2D位圖
  • 使用D2D繪制位圖

在開始之前,首先簡要介紹一下WIC

什么是WIC?

WIC全稱是Windows Image Component,是一套擴展的API,用來處理數字圖像,它是基於COM組件的。該API包含非常豐富的圖像處理函數,比如

  • 內置對於標准web image格式的解碼支持
  • 內置對於標准metadata格式的支持
  • 廣泛的像素格式支持
  • 高色度支持,包含30位擴展,30位及48位高精度像素格式
  • 對於圖像解碼,像素格式及元數據格式的擴展框架支持

WIC包含的組件及每個組件中的接口如下圖所示。

在這里,我們只要知道WIC能夠處理圖像即可,比如位圖操作。關於WIC的詳細信息,大家可以看看MSDN的介紹。

具體步驟

從文件創建WIC位圖

給定一個圖像文件,我們首先使用WIC函數將其讀入內存,並創建一個WIC類型的位圖。

首先我們需要創建一個解碼器,因為圖片是經過編碼的,為了能顯示圖片,我們首先需要將其解碼,創建解碼器需要使用函數CreateDecoderFromFilename,該函數返回一個解碼器指針。稍后的操作都通過這個指針來完成,關於這個函數的詳細介紹,可以參考MSDN,這里不再贅述。

然后,利用創建好的解碼器來獲取圖片的幀,我么這里只要第一幀,因為圖片只有一幀,但是對於視頻文件來說,就有許多幀了。代碼如下:在這里,uri即圖片文件名。

HRESULT LoadBitmapFromFile(
                           ID2D1RenderTarget *pRenderTarget,
                           IWICImagingFactory *pIWICFactory,
                           PCWSTR uri,
                           UINT destinationWidth,
                           UINT destinationHeight,
                           ID2D1Bitmap **ppBitmap
                           )
{
    HRESULT hr = S_OK;

    IWICBitmapDecoder *pDecoder = NULL;
    IWICBitmapFrameDecode *pSource = NULL;
    IWICStream *pStream = NULL;
    IWICFormatConverter *pConverter = NULL;
    IWICBitmapScaler *pScaler = NULL;

    hr = pIWICFactory->CreateDecoderFromFilename(
        uri,
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnLoad,
        &pDecoder
        );
    if (SUCCEEDED(hr))
    {

        // Create the initial frame.
        hr = pDecoder->GetFrame(0, &pSource);
    }

然后創建converter,負責對位圖進行后續的格式轉換。

if (SUCCEEDED(hr))
{
    hr = pIWICFactory->CreateFormatConverter(&pConverter);
}

接下來則要判斷圖像是否被放大或者縮小了,比如一個圖片的原始尺寸是100 x 100,但是我們程序中要以 200 x 200的方式去顯示,那么相當於將圖片放大了一倍,圖片的顯示尺寸通過參數來指定,而實際尺寸則是通過分析圖片文件得到。如果圖片有縮放,那么需要從新生成圖片的數據文件,如果沒有,那么直接進行下一步即可。代碼如下:

// If a new width or height was specified, create an
// IWICBitmapScaler and use it to resize the image.
if (destinationWidth != 0 || destinationHeight != 0)
{
    UINT originalWidth, originalHeight;
    hr = pSource->GetSize(&originalWidth, &originalHeight);
    if (SUCCEEDED(hr))
    {
        if (destinationWidth == 0)
        {
            FLOAT scalar = static_cast<FLOAT>(destinationHeight) / static_cast<FLOAT>(originalHeight);
            destinationWidth = static_cast<UINT>(scalar * static_cast<FLOAT>(originalWidth));
        }
        else if (destinationHeight == 0)
        {
            FLOAT scalar = static_cast<FLOAT>(destinationWidth) / static_cast<FLOAT>(originalWidth);
            destinationHeight = static_cast<UINT>(scalar * static_cast<FLOAT>(originalHeight));
        }

        hr = pIWICFactory->CreateBitmapScaler(&pScaler);
        if (SUCCEEDED(hr))
        {
            hr = pScaler->Initialize(
                pSource,
                destinationWidth,
                destinationHeight,
                WICBitmapInterpolationModeCubic
                );
        }
        if (SUCCEEDED(hr))
        {
            hr = pConverter->Initialize(
                pScaler,
                GUID_WICPixelFormat32bppPBGRA,
                WICBitmapDitherTypeNone,
                NULL,
                0.f,
                WICBitmapPaletteTypeMedianCut
                );
        }
    }
}

由WIC位圖創建D2D位圖

調用函數CreateBitmapFromWicBitmap可以由一個WIC位圖創建一個D2D位圖,代碼如下:

if (SUCCEEDED(hr))
{
    // Create a Direct2D bitmap from the WIC bitmap.
    hr = pRenderTarget->CreateBitmapFromWicBitmap(
        pConverter,
        NULL,
        ppBitmap
        );
}

上面這些代碼有個特點,就是要時刻判斷前一次函數調用的返回值,只有前面的操作成功了,才進行下一步操作。這是很好的編程習慣。

最后,需要做一些清理工作,由於WIC是基於COM的,所以,需要手動釋放COM對象,代碼如下:

SAFE_RELEASE(pDecoder);
SAFE_RELEASE(pSource);
SAFE_RELEASE(pStream);
SAFE_RELEASE(pConverter);
SAFE_RELEASE(pScaler);

SAFE_RELEASE是一個宏定義

#define SAFE_RELEASE(P) if(P){P->Release() ; P = NULL ;}

使用D2D繪制位圖

這一步就很簡單了,繪制位圖和繪制其他幾何圖形幾乎沒有區別。首先是將render target清空為指定顏色,也就是背景色,然后調用render target的接口DrawBitmap來繪制位圖,這個函數需要指定位圖的尺寸,所以之前還需要獲取位圖的大小。注意繪制代碼要放在BeginDraw和EndDraw之間。

void DrawBitmap()
{
    CreateD2DResource(g_Hwnd) ;

    pRenderTarget->BeginDraw() ;

    // Clear background color to dark cyan
    pRenderTarget->Clear(D2D1::ColorF(D2D1::ColorF::White));

    D2D1_SIZE_F size = pBitmap->GetSize() ;
    D2D1_POINT_2F upperLeftCorner = D2D1::Point2F(0.f, 0.f) ;

    // Draw bitmap
    pRenderTarget->DrawBitmap(
        pBitmap,
        D2D1::RectF(
        upperLeftCorner.x,
        upperLeftCorner.y,
        upperLeftCorner.x + size.width,
        upperLeftCorner.y + size.height)
        ) ;

    HRESULT hr = pRenderTarget->EndDraw() ;
    if (FAILED(hr))
    {
        MessageBox(NULL, "Draw failed!", "Error", 0) ;

        return ;
    }
}

最后,來一張效果圖,這是微軟的游戲 4 Elements 2 的截圖,大家一同欣賞一下。這是我平生購買的第一款游戲,值得紀念一下。

==

Happy Coding!!! 永遠不要放棄自己的夢想。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM