关闭

从GDI到Direct2D:贴图

标签: direct2DGDI
607人阅读 评论(1) 收藏 举报
分类:

距离上一篇文章更新已经很长时间了,那个游戏也做完了,能跑起来,联网效果也很好(没错那是一个网络游戏),有时间的话也会考虑把怎么做一个稍具规模的游戏的笔记贴出来吧,这里我们讲怎么用Direct2D贴图。

GDI里面,有一个类叫CImage,特别方便,想要读什么图片只需要Load一下,然后想画的时候,只需要说一句some_image.Draw(pDC, ...)就可以了,然而可惜的是D2D里面没有那么好用的类,不过这并不是什么问题,我们可以先看看这些东西怎么做,然后封装一个类出来就好了。

其实一般来讲,贴图只有两个主要的步骤:

  • 把图片读进来
  • 把图片贴在屏幕上

把图片读进来其实并不像想象中的那么简单。还记得IWICImagingFactory这个类吗?这个类就是负责加载外部图片资源的。和GDI不一样的是,加载图片资源的时候需要指定RenderTarget,你读进来的图片是和RenderTarget绑定的,这很可能令从GDI转过来的人感到不适应,因为GDI里面读图片的时候根本不要求指定DC。读进来以后,图片需要经过一系列的转换,最后存到一个叫ID2D1Bitmap的东西里面。整体来看,读一个图片的方法是这样的:

//三个参数,第一个是IWICImagingFactory的指针,第二个是RenderTarget的指针,第三个是图片的路径
HRESULT CD2DImage::Load(IWICImagingFactory * pIWICFactory,ID2D1RenderTarget * pd2dDevice,LPCTSTR pszResource)
{
    //重复操作
    Release();

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

    hr = pIWICFactory->CreateDecoderFromFilename(
        pszResource,
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnLoad,
        &pDecoder
        );

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

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

    if (SUCCEEDED(hr))
    {
        hr = pConverter->Initialize(
            pSource,
            GUID_WICPixelFormat32bppPBGRA,
            WICBitmapDitherTypeNone,
            NULL,
            0.f,
            WICBitmapPaletteTypeMedianCut
            );
    }

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

    SafeRelease(&pDecoder);
    SafeRelease(&pSource);
    SafeRelease(&pStream);
    SafeRelease(&pConverter);
    SafeRelease(&pScaler);

    if(SUCCEEDED(hr)) 
        m_bLoad=true;

    return hr;
}

好,现在你把图片读进来了,怎么贴图呢?读进来了贴图就好办了,因为RenderTarget有DrawBitmap函数,专门负责把刚才的ID2D1Bitmap给画在RenderTarget里面。

//绘画图像
HRESULT CD2DImage::DrawImage(ID2D1RenderTarget * pd2dDevice, float fXPos, float fYPos,D2D_SIZE_F WindowSize,float fOpacity)
{
    //状态判断
    if(IsValid()==false) return E_FAIL;
    if(pd2dDevice==NULL) return E_FAIL;

    float fWidth=WindowSize.width;
    float fHeight=WindowSize.height;

    //DrawBitmap的参数:ID2D1Bitmap的指针,一个D2D1::RectF指定目标矩形,不透明度
    pd2dDevice->DrawBitmap( m_pID2D1Bitmap,D2D1::RectF(fXPos,fYPos,fXPos+fWidth,fYPos+fHeight) ,fOpacity);

    return S_OK;
}

这样,我们只需要在必要的时候像调用Draw一样,使用DrawImage就可以了。

其实刚才这些就是我们自己封装的类D2D1Image中最重要的两个方法,它的全貌如下:

//D2D1Image.h

#pragma once
#include "stdafx.h"
#include "D2D1Header.h"

//image
class _declspec(dllexport) CD2DImage 
{
    //加载标志
private:
    bool                    m_bLoad;                //是否加载
    ID2D1Bitmap *           m_pID2D1Bitmap;         //位图标志

    //函数定义
public:
    //构造函数
    CD2DImage();
    //析构函数
    virtual ~CD2DImage();

    //状态函数
public:
    //是否加载
    bool IsValid();
    //获取宽度
    float GetWidth();
    //获取高度
    float GetHeight();

    //管理函数
public:
    //销毁图片
    void Release();
    //加载图片
    HRESULT Load(IWICImagingFactory * pIWICFactory,ID2D1RenderTarget* pd2dDevice,LPCTSTR pszResource);

    //绘画函数
public:
    //绘画图像
    HRESULT DrawImage(ID2D1RenderTarget * pd2dDevice, float fXPos, float fYPos,D2D_SIZE_F WindowSize,float fOpacity=1.0f);
    //绘画图像
    HRESULT DrawImage(ID2D1RenderTarget * pd2dDevice, float fXPos, float fYPos,float fXScale=1.0f,float fYScale=1.0f,float fOpacity=1.0f);
    //绘画图像
    HRESULT DrawImage(ID2D1RenderTarget * pd2dDevice, float fXDest, float fYDest, float fDestWidth, float fDestHeight, float fXScr, float fYSrc,float fXScale=1.0f,float fYScale=1.0f,float fOpacity=1.0f);

    //操作重载
public:
    //指针重载
    //ID2D1Bitmap * operator->() { return GetImage(); }
    //获取对象
    ID2D1Bitmap * GetImage() { return m_pID2D1Bitmap; }

};
//D2D1Image.cpp

#include "stdafx.h"
#include "D2D1Header.h"
#include "d2dimage.h"


//构造函数
CD2DImage::CD2DImage()
{
    m_bLoad=false;
    m_pID2D1Bitmap=NULL;
}

//析构函数
CD2DImage::~CD2DImage()
{
    Release();
}

//是否加载
bool CD2DImage::IsValid()
{
    return m_bLoad&&(m_pID2D1Bitmap);
}

//获取宽度
float CD2DImage::GetWidth()
{
    //加载判断
    if (!IsValid())
        return 0;

    //获取宽度
    return m_pID2D1Bitmap->GetSize().width;
}

//获取高度
float CD2DImage::GetHeight()
{
    //加载判断
    if (!IsValid())
        return 0;

    //获取宽度
    return m_pID2D1Bitmap->GetSize().height;
}

//销毁图片
void CD2DImage::Release()
{
    m_bLoad=false;
    SafeRelease(&m_pID2D1Bitmap);
}

//加载图片
HRESULT CD2DImage::Load(IWICImagingFactory * pIWICFactory,ID2D1RenderTarget * pd2dDevice,LPCTSTR pszResource)
{
    //重复操作
    Release();

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

    hr = pIWICFactory->CreateDecoderFromFilename(
        pszResource,
        NULL,
        GENERIC_READ,
        WICDecodeMetadataCacheOnLoad,
        &pDecoder
        );

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

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

    if (SUCCEEDED(hr))
    {
        hr = pConverter->Initialize(
            pSource,
            GUID_WICPixelFormat32bppPBGRA,
            WICBitmapDitherTypeNone,
            NULL,
            0.f,
            WICBitmapPaletteTypeMedianCut
            );
    }

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

    SafeRelease(&pDecoder);
    SafeRelease(&pSource);
    SafeRelease(&pStream);
    SafeRelease(&pConverter);
    SafeRelease(&pScaler);

    if(SUCCEEDED(hr)) 
        m_bLoad=true;

    return hr;
}

//绘画图像
HRESULT CD2DImage::DrawImage(ID2D1RenderTarget * pd2dDevice, float fXPos, float fYPos,D2D_SIZE_F WindowSize,float fOpacity)
{
    //状态判断
    if(IsValid()==false) return E_FAIL;
    if(pd2dDevice==NULL) return E_FAIL;

    float fWidth=WindowSize.width;
    float fHeight=WindowSize.height;
    pd2dDevice->DrawBitmap( m_pID2D1Bitmap,D2D1::RectF(fXPos,fYPos,fXPos+fWidth,fYPos+fHeight) ,fOpacity);

    return S_OK;
}

//绘画图像
HRESULT CD2DImage::DrawImage(ID2D1RenderTarget * pd2dDevice, float fXPos, float fYPos,float fXScale,float fYScale,float fOpacity)
{
    //状态判断
    if(IsValid()==false) return E_FAIL;
    if(pd2dDevice==NULL) return E_FAIL;

    float fWidth=GetWidth()*fXScale;
    float fHeight=GetHeight()*fYScale;
    pd2dDevice->DrawBitmap( m_pID2D1Bitmap,D2D1::RectF(fXPos,fYPos,fXPos+fWidth,fYPos+fHeight) ,fOpacity);

    return S_OK;
}

//绘画图像
HRESULT CD2DImage::DrawImage(ID2D1RenderTarget * pd2dDevice, float fXPos, float fYPos, float fDestWidth, float fDestHeight, float fXScr, float fYSrc,float fXScale,float fYScale,float fOpacity)
{
    //状态判断
    if(IsValid()==false) return E_FAIL;
    if(pd2dDevice==NULL) return E_FAIL;

    pd2dDevice->DrawBitmap( 
                                m_pID2D1Bitmap, D2D1::RectF(fXPos,fYPos,fDestWidth*fXScale+fXPos,fDestHeight*fYScale+fYPos) ,
                                fOpacity,D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
                                D2D1::RectF(fXScr,fYSrc,fXScr+fDestWidth,fYSrc+fDestHeight)
                            );

    return S_OK;
}

这里面的贴图函数有三个重载,基本的贴图效果都可以很轻易地实现。

然后,你只需要在建立好设备无关资源以后,声明好D2D1Image对象,之后在读取设备相关资源的时候,加载图片即可。是不是很简单了?

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:23483次
    • 积分:723
    • 等级:
    • 排名:千里之外
    • 原创:50篇
    • 转载:0篇
    • 译文:0篇
    • 评论:6条
    最新评论