从GDI到Direct2D:贴图

原创 2015年07月06日 21:03:19

距离上一篇文章更新已经很长时间了,那个游戏也做完了,能跑起来,联网效果也很好(没错那是一个网络游戏),有时间的话也会考虑把怎么做一个稍具规模的游戏的笔记贴出来吧,这里我们讲怎么用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对象,之后在读取设备相关资源的时候,加载图片即可。是不是很简单了?

Direct2D教程(九)渲染位图

概述这篇的标题更确切的说应该叫位图画刷,这样才好和前几篇对应起来。在Direct2D中,位图的渲染也是通过画刷来实现的。Direct2D中并没有直接操作位图的接口,而是借助WIC(Windows Im...
  • Augusdi
  • Augusdi
  • 2013年06月06日 23:35
  • 2773

MFC 用 Direct2D 显示图像到 Picture Control 中

2016年第一天,新年快乐!!!     由于一直跟视频这块打交道,对于图像编解码和显示等方面都有所研究。最近遇到一个性能要求比较高的应用,原本的GDI绘图导致到线程负荷比较重,造成整个系统不稳定,因...

Direct2D图片处理

Using Bitmap BrushesDirect2D 中的图片处理增加了很多的灵活的特性,现在的Direct2D可以很好的和GUI,GUI+以及Direct3D混合使用,它支持更多的图形格式,更丰...
  • Augusdi
  • Augusdi
  • 2013年06月06日 16:21
  • 5144

Direct 2D 加载位图.

HRESULT LoadImageFormFile( ID2D1RenderTarget *pRT, ...

Direct2D 第5篇 绘制图像

我加载的图像是一张透明底PNG图像,背景使用渐变的绿色画刷 #include #include #include #include #pragma comment(l...

关于GDI、D2D、Windows7的图形架构

关于GDI、D2D、D3D、Windows7的图形架构

GPU渲染时代——2D图形图像中的应用(一)

Windows上的2D程序还主要基于GDI和GDI+,遗憾的是这两者都是软件实现,而Direct2D则不同,它基于Direct3D,所以能够使用硬件加速功能,能更大程度的发挥硬件特性,创建高性能,高质...

GPU渲染时代——2D图形图像中的应用(二)

这篇博文来介绍DX的初始化,并封装成

Direct2D WIC绘制图片

绘制图片需要用到WIC,WIC的功能包括: 编解码图片。也可以自定义图片解码插件。读取图片元数据。 图像处理(最高支持每通道32位)。内置支持一些流行的格式。包括:BMP v5, GIF 89a/...
  • cuit
  • cuit
  • 2013年07月28日 15:55
  • 2054

Direct2D+MFC学习笔记

Direct2D 学习,包含环境配置及MFC画图
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:从GDI到Direct2D:贴图
举报原因:
原因补充:

(最多只允许输入30个字)