Delphi 7中对StretchBlt, StretchDIBits, DrawDibDraw, BitBlt 的性能测试 - 原创
我的天哪,上一篇博文是2年前的事情了。看来又虚度了2年光阴,继续学习。。。
本文算是副产品,正品是利用 FFmpeg 从任意视频中生成 GIF 片段的小程序,等写完了再发。不为别的,只是为了给儿子做动图,且看不惯这种工具也要收费!
V2G 正品已出炉,虽然不大像样,但好歹是能用,请见:用 Delphi 7 实现基于 FFMS2 的视频转 GIF 工具。
声明
本文是首先看到了求比 Stretchblt 方法更快的缩放算法的帖子,请参看其中署名为“张辉明”的回复。我做了优化和一些修正,但 DrawDibDraw 部分的调用是原文照录的。(其实上文就是我 Bing 了 DrawDibDraw 时搜到的。)
为什么要测试 StretchBlt、StretchDIBits、DrawDibDraw 的性能
因为视频回放需要很高的显示性能,解码占了很多计算量,留给显示的时间不多,能优化则优化吧。
其实现在的 CPU 跑个视频播放已经绰绰有余了,GPU 压根就不必用。即便是用 Delphi 自带的 TImage 控件,用 Bitmap 往里填也可以满足普通播放需求了。如果时光倒流到 10 年前,那可真是得去研究 DirectX、OpenGL了。可惜关于这哥俩,大部分都是 C、C++ 的资源,我啃了半天 SDL,觉得有点杀鸡用牛刀。所以就想着先实现需求吧,真的不行了再优化吧。在我的 Intel i3 3220 上,用 StretchDIBits 播放视频时最多也就跑了 22%。
为什么还抱着 Delphi 不放?
- 性价比第一
敢问性能、便捷、体积俱佳的 Windows 开发环境,谁敢和 Delphi 比?C#,Java 是优秀,可为了一个小功能就跑它个虚拟机,实在划不来啊。C++ 倒是够 sharp,可学习过程太痛苦了,代码还不容易写。 - 全能
都说 Python 好,可我眼拙,实在看不出来好在哪里,局限性太大。唯一的好处是能让新手快速上手编程,还有一个好处是能让你忘记计算机是怎么运作的! - 怀旧
十几年前自学的东西,从 Delphi 3 开始用,有感情了。只要 Windows 不停止对 32 位程序的支持,我就会一直用下去。(关于这一点,我要狠狠鄙视 Apple 一下。) - Delphi 7是经典
和 Visual Studio、水果一样,当年 Borland 的产品也有大小年,逢单的版本就是稳定一些。虽然轮子有时候得从头开始造,但是“知其所以然”是乐在其中的事,相信我!
测试结果
如果只关心结果,或者对 Delphi 不屑,那您就不必往下看了,我先给出结果吧。为您节省点时间。严格意义上说,BitBlt不属于其他哥仨的阵营,因为不用缩放,所以速度当然快了。放在这里比较,就当是个 Baseline 吧。
- DrawDibDraw 最快(1ms 级别)。
不到 StretchBlt和StretchDIBits 的一半,且不需要用 SetStretchBltMode 设置什么缩放模式,画质看不出分别。 - StretchBlt 和 StretchDIBits 难分伯仲。
用了色彩拟合模式(HALFTONE)的话会大大增加计算量,耗时4倍,比 DrawDibDraw 慢1个数量级。建议缩小图像时可以用 COLORONCOLOR 模式,肉眼看不出区别,但可以比 HALFTONE 模式提速4倍!
API | COLORONCOLOR | HALFTONE |
---|---|---|
BitBlt | 400 | 400 |
DrawDibDraw | 1125 | 1125 |
StretchBlt | 3000 | 11406 |
StretchDIBits | 3203 | 11576 |
- 测试用机:CPU: Intel i3 3220,内存: 8G DDRIII 1333,显卡: AMD Radeon HD 7700 (对测试结果没影响吧),Windows 10专业版
- 测试次数:1000次
- 时间单位:millisecond(毫秒)
- COLORONCOLOR:删除不需要的点。
这是 SetStretchBltMode 的参数,指定目标设备(区域)的缩放模式。在用 StretchDIBits 和 StretchBlt 时必须得设置一个缩放模式,不然,嘿嘿,惨不忍睹。官方说明是:“Deletes the pixels. This mode deletes all eliminated lines of pixels without trying to preserve their information.”,中文意思大概就是:删除不需要的像素点。该模式删除所有无用的点阵,这些点的所有信息都不予保留。 参见 SetStretchBltMode。 - HALFTONE:将源区域的颜色溶入目标区域中去。
作用同上。官方说明是:“Maps pixels from the source rectangle into blocks of pixels in the destination rectangle. The average color over the destination block of pixels approximates the color of the source pixels.”中文大概意思是:将源矩形区域的像素点信息拟合到目标区域周边的多个像素块中。目标区域多个像素块的颜色值会进行平均,以便最大程度地接近源像素的色彩。参见 SetStretchBltMode。
源码
界面
就放了几个按钮而已,名称末尾为C的表示用了 COLORONCOLOR 模式,为H的表示用了 HALFTONE 模式。还有一个 Timage 控件。
常量
FileName 定义了 Bmp 图片文件名,Count 定义了测试循环的次数。
FileName='1.bmp';
Count=1000;
FontSize=20;
BMP 文件读取
因为 StretchBlt和BitBlt 只需要提供源 HDC,不需要用 tagBITMAPINFO 和原始 RGB 数据区作为参数,所以直接用了 TBitmap 控件载入图片文件。
procedure TMainForm.StretchBltDisplay;
var
bmp : TBitmap ;
i : Integer ;
Start : DWORD ;
begin
Bmp:= TBitmap.Create ;
bmp.LoadFromFile(FileName);
Start := GetTickCount ;
for i := 1 to count do
begin
StretchBlt(image1.Canvas.Handle, 0, 0, image1.ClientWidth, image1.ClientHeight,
bmp.Canvas.Handle, 0,0,bmp.Width,bmp.Height, SRCCOPY);
image1.Canvas.TextOut(10,10,inttostr(i));
image1.Refresh;
end;
MainForm.Caption := IntToStr(GetTickCount - Start);
bmp.Free ;
end;
DrawDibDraw和DrawDibDraw都需要用到BMP原始信息做参数,所以只好写了个LoadBmp从文件中读取数据。
因为要把原始信息带出去,所以带了var前缀。
procedure LoadBmp(bmpFile: String; var bmpinfo:TBitmapInfo; var pBmpData:Pointer);
var
bmf: TBitmapFileHeader;
imageSize: LongWord;
Stream: TFileStream;
begin
try
Stream:= TFileStream.Create(bmpFile, fmOpenRead or fmShareDenyWrite);
Stream.Read(bmf, sizeof(Bmf));
Stream.Read(bmpinfo, sizeof(bmpinfo));
imageSize:= bmf.bfSize-bmf.bfOffBits;
stream.Seek(bmf.bfOffBits,0);
FreeMem(pBmpData);
GetMem(pBmpData, imageSize);
Stream.Read(pBmpData^, ImageSize);
finally
FreeAndNil(Stream);
end;
end;
关于 var 前缀
一开始以为,用指针就可以在函数内给外部的指针分配内存并传出结果了。但其实不对,外面的指针还一直是 nil。必须带上 var 前缀才行(指针的指针)。
关于 VFW
DrawDibDraw 是 VFW(Video for Windows)中的 API,关于 DrawDibDraw 的用法可以参考园子里的 DrawDibDraw函数的使用方法。封装文件 VFW.pas 来自一篇《delphi 摄像头编程 vfw》,出处已不可考,被署名 Tom Nuydens 的修改过。
完整源码
结论和建议
- 单纯缩小画面的(源图一定比目标图大):StretchBlt、StretchDIBits 随便用,先用 SetStretchBltMode 选 COLORONCOLOR 模式,性能足够了。
- 必须放大画面的(源图比目标图小):要用StretchBlt、StretchDIBits,用SetStretchBltMode必须选HALFTONE模式。性能无法接受可选 DrawDibDraw。
- 图省事用 DrawDibDraw,可能要多耗些资源吧(没精确测算过)。
- 图形性能要求更高的,啃DirectX、OpenGL、SDL去吧。代码不难,难的是要理解那么多图形学概念。
Delphi 7中对StretchBlt, StretchDIBits, DrawDibDraw, BitBlt 的性能测试 - 原创的更多相关文章
- delphi项目中的modelsupport文件夹
delphi项目中的modelsupport文件夹 今天写着写着突然发现多了一个这个文件夹..苦思不得其解 看着又难受 删了又重建 终于找到了 存此备查;Tools--option--toget ...
- Fastreport使用经验(转)在Delphi程序中访问报表对象
Fastreport使用经验(转) 在Delphi程序中访问报表对象 最基本的方法就是frxReport1.FindObject. 然后把返回的对象强制转换成它的类型,当然,在报表中必须真的有这么个东 ...
- 【转】资源文件在Delphi编程中的应用
段东宁 计亚南 (郴州职业技术学院, 湖南 郴州 423000) 摘要: 资源文件是一种能有效地组织.管理和使用资源的文件形式,在软件开发中有着广泛的应用.本文详细介绍了在Delphi编程中资源文件 ...
- 关于delphi XE7中的动态数组和并行编程(第一部分)
本文引自:http://www.danieleteti.it/category/embarcadero/delphi-xe7-embarcadero/ 并行编程库是delphi XE7中引进的最受期待 ...
- TMsgThread, TCommThread -- 在delphi线程中实现消息循环
http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...
- 远程控制篇:在DELPHI程序中拨号上网
用MODEM拨号上网,仍是大多数个人网民选择上网的方式.如果能在我们的应用程序中启动拨号连接(如IE浏览器程序中的自动拨号功能),无疑将会方便我们的软件用户(不用再切换应用程序,运行拨号网络),提高我 ...
- Delphi Format中的换行符号是什么
Delphi Format中的换行符号是什么 #,s1]); s3#'%s',[s,s1]); ShowMessage(s2); ShowMessage(s3); end; #13#10两边 ...
- Delphi代码中嵌入ASM代码
前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入ASM代码的程序员我想不多,因为这方面的资料太少了,另一方面,它还需要有基本的汇编语言知识,关於汇编语言的 ...
- delphi 7中使用idhttp抓取网页 解决假死现象
在delphi 7中使用idhttp抓取网页,造成窗口无反应的假死状态.通过搜索获得两种方法. 1.写在线程中,但是调用比较麻烦 2.使用delphi 提供的idantifreeze(必须安装indy ...
随机推荐
- LeetCode 624. Maximum Distance in Arrays (在数组中的最大距离)$
Given m arrays, and each array is sorted in ascending order. Now you can pick up two integers from t ...
- Kotlin——最详细的接口使用、介绍
在Kotlin中,接口(Interface)的使用和Java中的使用方式是有很大的区别.不管是接口中的属性定义,方法等.但是定义方式还是相同的. 目录 一.接口的声明 1.接口的声明 关键字:inte ...
- 读书笔记-你不知道的JS上-声明提升
变量声明提升 Javascript代码一般情况下是由上往下执行的,但是有些情况下不成立. a = 2; //变量声明被提升在当前作用域顶部 var a; console.log(a); console ...
- windows 上rsync客户端使用方法
1.1 获取 windows上实现rsync的软件(cwRsync) cwRsync是Windows 客户端GUI的一个包含Rsync的包装.您可以使用cwRsync快速远程文件备份和同步. 1.1. ...
- html5客户端本地存储之sessionStorage及storage事件
首先您可以看一下<JavaScript本地存储实践(html5的localStorage和ie的userData)>sessionStorage和上文中提到的localStorage非常相 ...
- vue 响应式原理
Vue 采用声明式编程替代过去的类 Jquery 的命令式编程,并且能够侦测数据的变化,更新视图.这使得我们可以只关注数据本身,而不用手动处理数据到视图的渲染,避免了繁琐的 DOM 操作,提高了开发效 ...
- C 程序实现密码隐秘输入 linux系统可执行
读写用户输入,屏幕不回显 char *getpass( const char *prompt); getpass用于从键盘读取用户输入,但屏幕不回显. 参数prompt为屏幕提示字符. 函数返回值为用 ...
- 07.十分钟学会tomcat数据源
一.数据源的作用及操作原理 1, 首先先看下传统JDBC的操作 在tomcat中使用数据库连接池操作数据库 2,JNDI属于命名及目录查找接口,在javaee的javax.naming包中 这套AP ...
- JSP技术介绍
1. 技术介绍 JSP即Java Server Page,中文全称是Java服务器语言.它是由Sun Microsystems公司倡导.许多公司参与建立的一种动态网页技术标准,它在动态网页的建设中有强 ...
- 三、VueJs 填坑日记之项目文件认识
上一篇博文,我们搭建了一套基础的vuejs的环境,首先安装node.js,然后利用npm包管理器,安装vue-cli,设置淘宝镜像,初始化项目,安装依赖,运行.在这一篇,我们将认识vuejs项目里的各 ...