从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 和 GDI 互操作性概述

必备条件将 Direct2D 内容绘制到 GDI 设备上下文ID2D1DCRenderTargets、GDI 转换以及从右向左书写语言版本的 Windows将 GDI 内容绘制到与 Direct2D ...
  • jinvmen
  • jinvmen
  • 2015年07月27日 11:33
  • 1322

从GDI到Direct2D:基本准备

最近在做个游戏,因为不能用游戏引擎,所以一开始就选了MFC+GDI的组合,毕竟CImage类是相当好用的,结果发现游戏竟然在还没有加什么功能的时候就只能跑到30帧出头,我觉得有点悬,将来如果加上更多的...
  • u011808175
  • u011808175
  • 2015年04月05日 21:17
  • 1348

加载GDI+,并且使用GDI+制作png贴图

1、加载GDI+  1)在stdafx.h中添加:    #include           using namespace Gdiplus;  2)在DLG的头文件里添加:   ...
  • yongzhen150
  • yongzhen150
  • 2014年12月03日 16:46
  • 699

GDI+ 与 GDI (平铺与拉伸)PNG背景图贴图片的方法

GDI+ 与 GDI (平铺与拉伸)PNG背景图贴图片的方法 GDI+是微软在Windows2000以后的操作系统中提供的新接口,它是基于面向对象的,而GDI是基于函数的。 建议大...
  • cddchina
  • cddchina
  • 2014年05月09日 14:45
  • 4748

Direct2d绘制图片的问题

网上看了不少资料,其中某篇是用笔刷画的。这样做仅适用于图片可以一次贴完的情况下。若想要贴图片的一部分 必须要用到rendertarget的 drawbimap方法。而不是用笔刷来画。如下的函数比较符合...
  • Augusdi
  • Augusdi
  • 2013年06月06日 16:28
  • 2428

[Direct2D]DX11中的2D显示

Dx11中的2D显示抛弃了Dx8和Dx9中的 ID3DxSprite 绘制精灵,也出乎意料的(出乎我的意料 :) )没有采用顶点方式而是返璞归真走向了表面。现在的Direct2D更像是GDI了~~~D...
  • m9551
  • m9551
  • 2010年06月25日 13:22
  • 3750

在DirectX快速显示图片

其实我觉得可能用D3D实现会更加好的。 具体的可以查一下D3DXSprite这个东西,也常常被称为“2D精灵”,是DirectX8.0以及更高版本中显示2D图片的最好方法之一。支持的文件格式也很...
  • xjmeng001
  • xjmeng001
  • 2010年12月03日 19:19
  • 1612

Direct2D 第5篇 绘制图像

我加载的图像是一张透明底PNG图像,背景使用渐变的绿色画刷 #include #include #include #include #pragma comment(l...
  • ubuntu_ai
  • ubuntu_ai
  • 2015年12月20日 22:08
  • 1597

Direct2D在文件夹加载图片获取位图和直接获取资源位图

  • 2013年12月16日 14:02
  • 741KB
  • 下载

从GDI到Direct2D:贴图

距离上一篇文章更新已经很长时间了,那个游戏也做完了,能跑起来,联网效果也很好(没错那是一个网络游戏),有时间的话也会考虑把怎么做一个稍具规模的游戏的笔记贴出来吧,这里我们讲怎么用Direct2D贴图。...
  • u011808175
  • u011808175
  • 2015年07月06日 21:03
  • 909
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:从GDI到Direct2D:贴图
举报原因:
原因补充:

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