概述

这篇的标题更确切的说应该叫位图画刷,这样才好和前几篇对应起来。在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 的截图,大家一同欣赏一下。这是我平生购买的第一款游戏,值得纪念一下。

Direct2D教程(九)渲染位图的更多相关文章

  1. SharpDX之Direct2D教程II——加载位图文件和保存位图文件

    本系列文章目录: SharpDX之Direct2D教程I——简单示例和Color(颜色) 绘制位图是绘制操作的不可缺少的一部分.在Direct2D中绘制位图,必须先利用WIC组件将位图加载到内存中,再 ...

  2. Direct2D教程V——位图(Bitmap)和位图笔刷(BitmapBrush)

    目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct ...

  3. Direct2D教程VIII——几何(Geometry)对象的运算,本系列的终结篇

    目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct ...

  4. Direct2D教程VII——变换几何(TransformedGeometry)对象

    目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct ...

  5. Direct2D教程VI——转换(Transform)

    目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct ...

  6. Direct2D教程IV——笔刷(Brush)对象

    目前博客园中成系列的Direct2D的教程有 1.万一的 Direct2D 系列,用的是Delphi 2009 2.zdd的 Direct2D 系列,用的是VS中的C++ 3.本文所在的 Direct ...

  7. CRL快速开发框架系列教程九(导入/导出数据)

    本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框 ...

  8. Direct2D教程(外篇)环境配置

    2014年世界杯首场淘汰赛马上开始了,闲着没事,整理以前的博客草稿打发时间,意外的发现这篇文章,本来是打算加入到Direct2D那个系列的,不知道为什么把它给遗漏了.环境配置,对于熟手来说,不是什么重 ...

  9. 无废话ExtJs 入门教程九[数字字段:NumberField、隐藏字段Hidden、日期字段:DataFiedl]

    无废话ExtJs 入门教程九[数字字段:NumberField.隐藏字段Hidden.日期字段:DataFiedl] extjs技术交流,欢迎加群(201926085) 继上第六节内容,我们在表单里加 ...

  10. 黄聪:Microsoft Enterprise Library 5.0 系列教程(九) Policy Injection Application Block

    原文:黄聪:Microsoft Enterprise Library 5.0 系列教程(九) Policy Injection Application Block 代理对象(Proxy Object) ...

随机推荐

  1. HDU 5525 Product 数论

    题意: 给出一个长度为\(n(1 \leq n \leq 10^5)\)的序列\(A_i\),\(N=\prod\limits_{i=1}^{n}i^{A_i}\).求\(N\)的所有约数的乘积. 分 ...

  2. luogu2763 试题库问题

    倘若某个试题已经被选到某个类型里了,那么它就不可再被选进别的类型了. 所以,对于每个类型,我们将其与汇连边,权值是它的要求的题目数量. 对于每个题目,我们将源与其连边,权值是1,代表只能用一次.然后再 ...

  3. ranorex前一步的操作结果后一步如何调用

     if (!TestSuite.Current.Parameters.ContainsKey("Password"))TestSuite.Current.Parameters.Ad ...

  4. CF750E 线段树+矩阵乘矩阵加

    题目描述 A string tt is called nice if a string "2017" occurs in tt as a subsequence but a str ...

  5. Pycharm脚本通用部分设置

    Python脚本经常要设置同样的注释内容,Pycharm里面提供的模板可以很好的实现这个需求. 查找: File->settings->Editor->File and Code T ...

  6. Hibernate框架简述(转)

    转自:http://www.cnblogs.com/eflylab/archive/2007/01/09/615338.html Hibernate的核心组件在基于MVC设计模式的JAVA WEB应用 ...

  7. 在Ubuntu中,用mvn打包hadoop源代码时报错,正在解决中!!!

    报错信息如下: (各种配置在最后面) hadoop@administrator-virtual-machine:~/Downloads/tar/hadoop-3.0.0-alpha1-src$ mvn ...

  8. Java面试题之HashMap如何有效减少碰撞

    1.扰动函数算法,促使元素位置分布均匀,减少碰撞几率: 2.使用final对象,并采用合适的equals方法和hashCode方法:

  9. Python ping 模块

    使用socket模块也可以获得域名对应的ip,参考:https://blog.csdn.net/c465869935/article/details/50850598 print socket.get ...

  10. bzoj 2788 [Poi2012]Festival 差分约束+tarjan+floyd

    题目大意 有n个正整数X1,X2,...,Xn,再给出m1+m2个限制条件,限制分为两类: 1.给出a,b (1<=a,b<=n),要求满足Xa + 1 = Xb 2.给出c,d (1&l ...