我的天哪,上一篇博文是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 不放?

  1. 性价比第一

    敢问性能、便捷、体积俱佳的 Windows 开发环境,谁敢和 Delphi 比?C#,Java 是优秀,可为了一个小功能就跑它个虚拟机,实在划不来啊。C++ 倒是够 sharp,可学习过程太痛苦了,代码还不容易写。
  2. 全能

    都说 Python 好,可我眼拙,实在看不出来好在哪里,局限性太大。唯一的好处是能让新手快速上手编程,还有一个好处是能让你忘记计算机是怎么运作的!
  3. 怀旧

    十几年前自学的东西,从 Delphi 3 开始用,有感情了。只要 Windows 不停止对 32 位程序的支持,我就会一直用下去。(关于这一点,我要狠狠鄙视 Apple 一下。)
  4. Delphi 7是经典

    和 Visual Studio、水果一样,当年 Borland 的产品也有大小年,逢单的版本就是稳定一些。虽然轮子有时候得从头开始造,但是“知其所以然”是乐在其中的事,相信我!

测试结果

如果只关心结果,或者对 Delphi 不屑,那您就不必往下看了,我先给出结果吧。为您节省点时间。严格意义上说,BitBlt不属于其他哥仨的阵营,因为不用缩放,所以速度当然快了。放在这里比较,就当是个 Baseline 吧。

  1. DrawDibDraw 最快(1ms 级别)。

    不到 StretchBlt和StretchDIBits 的一半,且不需要用 SetStretchBltMode 设置什么缩放模式,画质看不出分别。
  2. StretchBltStretchDIBits 难分伯仲。

    用了色彩拟合模式(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 的性能测试 - 原创的更多相关文章

  1. delphi项目中的modelsupport文件夹

    delphi项目中的modelsupport文件夹 今天写着写着突然发现多了一个这个文件夹..苦思不得其解  看着又难受  删了又重建 终于找到了  存此备查;Tools--option--toget ...

  2. Fastreport使用经验(转)在Delphi程序中访问报表对象

    Fastreport使用经验(转) 在Delphi程序中访问报表对象 最基本的方法就是frxReport1.FindObject. 然后把返回的对象强制转换成它的类型,当然,在报表中必须真的有这么个东 ...

  3. 【转】资源文件在Delphi编程中的应用

    段东宁 计亚南 (郴州职业技术学院, 湖南 郴州  423000) 摘要: 资源文件是一种能有效地组织.管理和使用资源的文件形式,在软件开发中有着广泛的应用.本文详细介绍了在Delphi编程中资源文件 ...

  4. 关于delphi XE7中的动态数组和并行编程(第一部分)

    本文引自:http://www.danieleteti.it/category/embarcadero/delphi-xe7-embarcadero/ 并行编程库是delphi XE7中引进的最受期待 ...

  5. TMsgThread, TCommThread -- 在delphi线程中实现消息循环

    http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...

  6. 远程控制篇:在DELPHI程序中拨号上网

    用MODEM拨号上网,仍是大多数个人网民选择上网的方式.如果能在我们的应用程序中启动拨号连接(如IE浏览器程序中的自动拨号功能),无疑将会方便我们的软件用户(不用再切换应用程序,运行拨号网络),提高我 ...

  7. Delphi Format中的换行符号是什么

    Delphi Format中的换行符号是什么 #,s1]);  s3#'%s',[s,s1]);  ShowMessage(s2);  ShowMessage(s3); end;   #13#10两边 ...

  8. Delphi代码中嵌入ASM代码

    前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入ASM代码的程序员我想不多,因为这方面的资料太少了,另一方面,它还需要有基本的汇编语言知识,关於汇编语言的 ...

  9. delphi 7中使用idhttp抓取网页 解决假死现象

    在delphi 7中使用idhttp抓取网页,造成窗口无反应的假死状态.通过搜索获得两种方法. 1.写在线程中,但是调用比较麻烦 2.使用delphi 提供的idantifreeze(必须安装indy ...

随机推荐

  1. 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 ...

  2. Kotlin——最详细的接口使用、介绍

    在Kotlin中,接口(Interface)的使用和Java中的使用方式是有很大的区别.不管是接口中的属性定义,方法等.但是定义方式还是相同的. 目录 一.接口的声明 1.接口的声明 关键字:inte ...

  3. 读书笔记-你不知道的JS上-声明提升

    变量声明提升 Javascript代码一般情况下是由上往下执行的,但是有些情况下不成立. a = 2; //变量声明被提升在当前作用域顶部 var a; console.log(a); console ...

  4. windows 上rsync客户端使用方法

    1.1 获取 windows上实现rsync的软件(cwRsync) cwRsync是Windows 客户端GUI的一个包含Rsync的包装.您可以使用cwRsync快速远程文件备份和同步. 1.1. ...

  5. html5客户端本地存储之sessionStorage及storage事件

    首先您可以看一下<JavaScript本地存储实践(html5的localStorage和ie的userData)>sessionStorage和上文中提到的localStorage非常相 ...

  6. vue 响应式原理

    Vue 采用声明式编程替代过去的类 Jquery 的命令式编程,并且能够侦测数据的变化,更新视图.这使得我们可以只关注数据本身,而不用手动处理数据到视图的渲染,避免了繁琐的 DOM 操作,提高了开发效 ...

  7. C 程序实现密码隐秘输入 linux系统可执行

    读写用户输入,屏幕不回显 char *getpass( const char *prompt); getpass用于从键盘读取用户输入,但屏幕不回显. 参数prompt为屏幕提示字符. 函数返回值为用 ...

  8. 07.十分钟学会tomcat数据源

    一.数据源的作用及操作原理 1,  首先先看下传统JDBC的操作 在tomcat中使用数据库连接池操作数据库 2,JNDI属于命名及目录查找接口,在javaee的javax.naming包中 这套AP ...

  9. JSP技术介绍

    1. 技术介绍 JSP即Java Server Page,中文全称是Java服务器语言.它是由Sun Microsystems公司倡导.许多公司参与建立的一种动态网页技术标准,它在动态网页的建设中有强 ...

  10. 三、VueJs 填坑日记之项目文件认识

    上一篇博文,我们搭建了一套基础的vuejs的环境,首先安装node.js,然后利用npm包管理器,安装vue-cli,设置淘宝镜像,初始化项目,安装依赖,运行.在这一篇,我们将认识vuejs项目里的各 ...