GDI+ 在Delphi程序的应用 -- 调整图像亮度
调整图像的亮度可以有很多方法,最常用的方法就是对图像像素点的R、G、B三个分量同时进行增加(减少)某个值,达到调整亮度的目的。我在这里使用GDI+图像的扫描线来处理,核心处理采用了2个相同的过程,一个是Pascal过程,一个是嵌入汇编过程,通过比较,对小的图像几乎没有什么区别,对比较大的图像处理还是有一定的区别(具体测试效果见代码中的注释),这说明Delphi的代码优化还是很好的。
使用GDI+图像的扫描线处理必须调用TGpBitmap.LockBits和TGpBitmap.UnLockBits过程,而这2个过程的调用相当耗时,以2304 *1728大小的图片为例,耗时竟达188毫秒!而亮度调整过程最长只耗时63毫秒。由此可以看出使用GDI+图像扫描线处理图像并非十分理想。
需要说明的是,本例子及以后GDI+ for Delphi例子的Gdiplus单元是本人自己写的,与目前网上流通的版本有一定的区别,浏览着需要测试例子,可作适当修改,或者发邮件向本人索取Gdiplus单元。
说明:为了统一《GDI+ 在Delphi程序的应用》系列文章所用数据类型和图像处理格式,本文代码已重新修订,代码中所用Gdiplus单元下载地址及BUG更正见文章《GDI+ for VCL基础 -- GDI+ 与 VCL》。(2008.8.18记)
数据类型:
- type
- //与GDI+TBitmapData结构兼容的图像数据结构
- TImageData=packedrecord
- Width:LongWord;//图像宽度
- Height:LongWord;//图像高度
- Stride:LongWord;//图像扫描线字节长度
- PixelFormat:LongWord;//未使用
- Scan0:Pointer;//图像数据地址
- Reserved:LongWord;//保留
- end;
- PImageData=^TImageData;
- //获取TBitmap图像的TImageData数据结构,便于处理TBitmap图像
- functionGetImageData(Bmp:TBitmap):TImageData;
- begin
- Bmp.PixelFormat:=pf32bit;
- Result.Width:=Bmp.Width;
- Result.Height:=Bmp.Height;
- Result.Scan0:=Bmp.ScanLine[Bmp.Height-1];
- Result.Stride:=Result.Widthshl2;
- //Result.Stride:=(((32*Bmp.Width)+31)and$ffffffe0)shr3;
- end;
过程代码:
- //调整图像亮度
- procedureBrightness(Data:TImageData;Value:Integer);
- asm
- pushesi
- pushedi
- movedi,[eax+16]
- movesi,[eax+4]
- imulesi,[eax]
- cld
- @PixelLoop:
- movecx,3
- @RGBLoop:
- movzxeax,[edi]
- addeax,edx
- jns@@1
- xoreax,eax
- jmp@@2
- @@1:
- cmpeax,255
- jle@@2
- moveax,255
- @@2:
- stosb
- loop@RGBLoop
- incedi
- decesi
- jnz@PixelLoop
- popedi
- popesi
- end;
- //调整GDI+图像亮度
- procedureGdipBrightness(Bmp:TGpBitmap;Value:Integer);
- var
- Data:TBitmapData;
- begin
- ifValue=0thenExit;
- Data:=Bmp.LockBits(GpRect(0,0,Bmp.Width,Bmp.Height),[imRead,imWrite],pf32bppARGB);
- try
- Brightness(TImageData(Data),Value);
- finally
- Bmp.UnlockBits(Data);
- end;
- end;
- //调整TBitmap图像亮度
- procedureBitmapBrightness(Bmp:TBitmap;Value:Integer);
- begin
- ifValue<>0then
- Brightness(GetImageData(Bmp),Value);
- end;
测试代码:
- //为方便初学者理解前面的BASM代码,写了个处理GDI+图像亮度的纯Pas代码过程
- //稍稍改动一下,也可用来处理TBitmap图像亮度
- procedureGdipBrightness_Pas(Bmp:TGpBitmap;Value:Integer);
- functionSetRGBValue(Rgb:Byte):Integer;
- begin
- Result:=Value+Rgb;//像素颜色分量+亮度值
- ifResult<0then//像素颜色分量>=0and<=255
- Result:=0
- elseifResult>255then
- Result:=255;
- end;
- var
- Data:TBitmapData;
- P:PRGBQuad;
- I,Count:LongWord;
- begin
- ifValue=0thenExit;
- Data:=Bmp.LockBits(GpRect(0,0,Bmp.Width,Bmp.Height),[imRead,imWrite],pf32bppARGB);
- try
- Count:=Data.Width*Data.Height;
- P:=Data.Scan0;
- forI:=1toCountdo
- begin
- P^.rgbBlue:=SetRGBValue(P^.rgbBlue);
- P^.rgbGreen:=SetRGBValue(P^.rgbGreen);
- P^.rgbRed:=SetRGBValue(P^.rgbRed);
- Inc(P);
- end;
- finally
- Bmp.UnlockBits(Data);
- end;
- end;
- //测试GDI+图像
- procedureTForm1.Button1Click(Sender:TObject);
- var
- Image:TGpBitmap;
- g:TGpGraphics;
- begin
- Image:=TGpBitmap.Create('D:\VclLib\GdiplusDemo\Media\20041001.jpg');
- g:=TGpGraphics.Create(Handle,False);
- g.DrawImage(Image,10,10);
- //GdipBrightness_Pas(Image,30);
- GdipBrightness(Image,30);
- g.DrawImage(Image,10,220);
- Image.Free;
- g.Free;
- end;
- //测试TBitmap图像
- procedureTForm1.Button2Click(Sender:TObject);
- var
- Image:TBitmap;
- begin
- Image:=TBitmap.Create;
- Image.LoadFromFile('D:\VclLib\GdiplusDemo\Media\20041001.bmp');
- Canvas.Draw(10,10,Image);
- BitmapBrightness(Image,-30);
- Canvas.Draw(10,220,Image);
- Image.Free;
- end;