使用dephi进行图像处理可以有多种方法,最常用的应该算是tbitmap,它提供方便的图像存取能力,结合canvas可进行画线、画圆、图像拷贝等操作。不过在进行大量的图像处理操作时,为了获得更高的速度,我们希望能够直接对图像缓冲区进行读写。查阅dephi的帮助手册没有发现直接取得整个图像缓冲区的功能,但提供的scanline属性可以取得指定行图像数据的指针,比较接近我们的要求,先看看scanline的描述:
provides indexed access to each line of pixels.
property scanline[row: integer]: pointer;
description
scanline is used only with dibs (device independent bitmaps) for image editing tools that do low-level pixel work.
让我们再看看scanline[0]、scanline[1]的关系:
procedure tform1.button1click(sender: tobject);
var
bitmap: tbitmap;
s: string;
begin
bitmap := tbitmap.create;
try
bitmap.pixelformat := pf24bit; //24位色,每像素点3个字节
bitmap.width := 1000;
bitmap.height := 2;
fmtstr(s, 'scanline[0]:%8x'#13'scanline[1]:%8x'#13'scanline[1]-scanline[0]:%d'
, [integer(bitmap.scanline[0]), integer(bitmap.scanline[1])
, integer(bitmap.scanline[1]) - integer(bitmap.scanline[0])]);
messagebox(handle, pchar(s), 'scanline', mb_ok);
finally
if assigned(bitmap) then freeandnil(bitmap);
end;
end;
下面是运行结果:
前两个结果因机器不同而不同,第三个结果很特别,scanline[0]与scanline[1]之间相差3000=1000像素宽×3字节这很容易理解,但为什么是负数呢?因为bmp图像数据是“按行存放,每行按双字对齐,行按倒序方式存放”的,也就是说屏幕显示的第一行存放在最后,屏幕显示的最后一行存放在前面,所以用acdsee等看图软件查看尺寸较大的位图时先从下部开始显示就是这个道理。
scanline[0]: e90bb8
scanline[1]: e90000
scanline[1]-scanline[0]:-3000
从上面的结果可以看出tbitmap的图像数据在内存中是按行倒序连续存放的,通过tbitmap.scanline[tbitmap.height-1]可以取得首地址即图像缓冲区地址。接着我们来实践一下,通过直接对图像缓冲区的读写将图像淡出到黑色:
| procedure tform1.button1click(sender: tobject); const fadeout_step = 24; //淡出衰减值 fix_width = 320; fix_height = 200; var bitmap: tbitmap; hwindc: hdc; flagagein: boolean; lpbuffer: pbyte; //图像缓冲区指针 begin bitmap := tbitmap.create; if not assigned(bitmap) then exit; try //设置位图格式、宽度、高度 bitmap.pixelformat := pf24bit; bitmap.width := fix_width; bitmap.height := fix_height; //设置form的宽充、高度,便于显示结果 button1.visible := false; clientwidth := fix_width; clientheight := fix_height; //拷贝图像到bitmap中 hwindc := getdc(0); if (hwindc<>null) then bitblt(bitmap.canvas.handle, 0, 0, fix_width, fix_height, hwindc, 0, 0, srccopy) else bitblt(bitmap.canvas.handle, 0, 0, fix_width, fix_height, canvas.handle, 0, 0, srccopy); repeat messagebox(handle, 'done', 'fadeout', mb_ok); |