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 = packed record
- Width: LongWord; // 图像宽度
- Height: LongWord; // 图像高度
- Stride: LongWord; // 图像扫描线字节长度
- PixelFormat: LongWord; // 未使用
- Scan0: Pointer; // 图像数据地址
- Reserved: LongWord; // 保留
- end;
- PImageData = ^TImageData;
- // 获取TBitmap图像的TImageData数据结构,便于处理TBitmap图像
- function GetImageData(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.Width shl 2;
- // Result.Stride := (((32 * Bmp.Width) + 31) and $ffffffe0) shr 3;
- end;
过程代码:
- // 调整图像亮度
- procedure Brightness(Data: TImageData; Value: Integer);
- asm
- push esi
- push edi
- mov edi, [eax + 16]
- mov esi, [eax + 4]
- imul esi, [eax]
- cld
- @PixelLoop:
- mov ecx, 3
- @RGBLoop:
- movzx eax, [edi]
- add eax, edx
- jns @@1
- xor eax, eax
- jmp @@2
- @@1:
- cmp eax, 255
- jle @@2
- mov eax, 255
- @@2:
- stosb
- loop @RGBLoop
- inc edi
- dec esi
- jnz @PixelLoop
- pop edi
- pop esi
- end;
- // 调整GDI+图像亮度
- procedure GdipBrightness(Bmp: TGpBitmap; Value: Integer);
- var
- Data: TBitmapData;
- begin
- if Value = 0 then Exit;
- 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图像亮度
- procedure BitmapBrightness(Bmp: TBitmap; Value: Integer);
- begin
- if Value <> 0 then
- Brightness(GetImageData(Bmp), Value);
- end;
测试代码:
- // 为方便初学者理解前面的BASM代码,写了个处理GDI+图像亮度的纯Pas代码过程
- // 稍稍改动一下,也可用来处理TBitmap图像亮度
- procedure GdipBrightness_Pas(Bmp: TGpBitmap; Value: Integer);
- function SetRGBValue(Rgb: Byte): Integer;
- begin
- Result := Value + Rgb; // 像素颜色分量 + 亮度值
- if Result < 0 then // 像素颜色分量 >= 0 and <= 255
- Result := 0
- else if Result > 255 then
- Result := 255;
- end;
- var
- Data: TBitmapData;
- P: PRGBQuad;
- I, Count: LongWord;
- begin
- if Value = 0 then Exit;
- Data := Bmp.LockBits(GpRect(0, 0, Bmp.Width, Bmp.Height), [imRead, imWrite], pf32bppARGB);
- try
- Count := Data.Width * Data.Height;
- P := Data.Scan0;
- for I := 1 to Count do
- 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+图像
- procedure TForm1.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图像
- procedure TForm1.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;