Delphi 10.4中带透明背景PNG图片裁剪绘制性能优化求助
Delphi 10.4下PNG透明图片裁剪绘制性能优化问题
我用Delphi 10.4在Windows 10上开发,碰到带透明背景的PNG图片裁剪绘制速度极慢的问题,核心原因是当前基于Scanline实现的裁剪逻辑性能太差。
原程序使用无透明背景的BMP时,通过CopyRect绘制裁剪区域仅耗时0.415ms,符合预期;但改用仅含全透明背景(无半透明像素)的PNG后,裁剪绘制耗时飙升至13.5ms。我尝试过用带Alpha通道的32位BMP结合AlphaBlend函数,但未成功实现。现寻求优化Scanline性能的方法,或合适的第三方库解决方案。
原BMP绘制代码
procedure TfrmMain.btnDrawBMPClick(Sender: TObject); var frontsigns : TBitmap; begin frontsigns := TBitmap.Create; frontsigns.LoadFromFile('E:\frontsigns.bmp'); frmMain.Canvas.CopyRect(Rect(0,0,302,869), frontsigns.Canvas, Rect(0, yStartPos, 302, yEndPos)); // yStartPos and yEndPos are variables end;
PNG绘制代码
procedure TfrmMain.btnDrawBMPClick(Sender: TObject); var background, tmpbackground : TBitmap; frontsigns, CroppedPng : TPngImage; begin background := TBitmap.Create; background.LoadFromFile('E:\back.bmp'); frontsigns := TPngImage.Create; frontsigns.LoadFromFile('E:\frontsigns.png'); tmpbackground := TBitmap.Create(303, 870); tmpbackground.Canvas.CopyRect(Rect(0, 0, 302, 869), background.Canvas, Rect(0, 0, 302, 869)); CropPng(frontsigns, 0, yStartPos, 302, yEndPos, CroppedPng); // yStartPos and yEndPos are variables tmpbackground.Canvas.Draw(0, 0, CroppedPng); end;
CropPng裁剪函数代码
const ColorTabMax = 10; ColorTab : array[0..ColorTabMax-1] of TColor = (ClBlack, ClMaroon, ClRed, ClWebDarkOrange, ClYellow, ClGreen, ClBlue, ClPurple, ClGray, ClWhite); procedure CropPng(Source: TPngImage; Left, Top, Width, Height: Integer; out Target : TPngImage); function ColorToTriple(Color: TColor): TRGBTriple; begin Color := ColorToRGB(Color); Result.rgbtBlue := Color shr 16 and $FF; Result.rgbtGreen := Color shr 8 and $FF; Result.rgbtRed := Color and $FF; end; var X, Y : Integer; Bitmap : TBitmap; BitmapLine : PRGBLine; AlphaLineA, AlphaLineB : pngImage.PByteArray; begin if (Source.Width < (Left + Width)) or (Source.Height < (Top + Height)) then raise Exception.Create('Invalid position/size'); Bitmap := TBitmap.Create; try Bitmap.Width := Width; Bitmap.Height := Height; Bitmap.PixelFormat := pf24bit; for Y := 0 to Bitmap.Height - 1 do begin BitmapLine := Bitmap.Scanline[Y]; for X := 0 to Bitmap.Width - 1 do BitmapLine^[X] := ColorToTriple(Source.Pixels[Left + X, Top + Y]); end; Target := TPngImage.Create; Target.Assign(Bitmap); if Source.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA] then begin Target.CreateAlpha; for Y := 0 to Target.Height - 1 do begin AlphaLineA := Source.AlphaScanline[Top + Y]; AlphaLineB := Target.AlphaScanline[Y]; for X := 0 to Target.Width - 1 do AlphaLineB^[X] := AlphaLineA^[X + Left]; end; end; finally Bitmap.Free; end; end;
优化方案
1. 替换Source.Pixels调用(核心性能提升)
当前裁剪函数中Source.Pixels[Left + X, Top + Y]是性能瓶颈——TPngImage.Pixels每次调用都会执行颜色转换、坐标校验等冗余操作,循环逐像素调用会放大开销。直接读取PNG原始扫描线数据,用内存拷贝代替逐像素赋值:
// 优化后的CropPng函数 procedure CropPng(Source: TPngImage; Left, Top, Width, Height: Integer; out Target : TPngImage); var X, Y: Integer; SourceRGBLine: PRGBLine; TargetRGBLine: PRGBLine; SourceAlphaLine, TargetAlphaLine: pngImage.PByteArray; begin if (Source.Width < (Left + Width)) or (Source.Height < (Top + Height)) then raise Exception.Create('Invalid position/size'); // 直接创建目标PNG,跳过TBitmap中转 Target := TPngImage.CreateBlank(COLOR_RGBALPHA, 8, Width, Height); for Y := 0 to Height - 1 do begin // 获取源和目标的RGB扫描线指针 SourceRGBLine := Source.Scanline[Top + Y]; TargetRGBLine := Target.Scanline[Y]; // 内存拷贝整行RGB数据,代替逐像素赋值 Move(SourceRGBLine^[Left], TargetRGBLine^[0], Width * SizeOf(TRGBTriple)); // 处理Alpha通道 if Source.Header.ColorType in [COLOR_GRAYSCALEALPHA, COLOR_RGBALPHA] then begin SourceAlphaLine := Source.AlphaScanline[Top + Y]; TargetAlphaLine := Target.AlphaScanline[Y]; Move(SourceAlphaLine^[Left], TargetAlphaLine^[0], Width); end; end; end;
2. 用32位BMP结合AlphaBlend直接绘制裁剪区域
由于你的PNG只有全透明/不透明像素,可直接将PNG转为32位带Alpha的BMP,用AlphaBlend一步完成裁剪+绘制,避免先裁剪再绘制的冗余步骤:
procedure TfrmMain.btnDrawPNGOptimizedClick(Sender: TObject); var background, frontBmp: TBitmap; frontsigns: TPngImage; srcRect, destRect: TRect; blendFunc: BLENDFUNCTION; begin background := TBitmap.Create; try background.LoadFromFile('E:\back.bmp'); background.PixelFormat := pf32bit; frontsigns := TPngImage.Create; try frontsigns.LoadFromFile('E:\frontsigns.png'); frontBmp := TBitmap.Create; try frontBmp.Assign(frontsigns); frontBmp.PixelFormat := pf32bit; srcRect := Rect(0, yStartPos, 302, yEndPos); destRect := Rect(0, 0, srcRect.Width, srcRect.Height); // 配置Alpha混合参数 blendFunc.BlendOp := AC_SRC_OVER; blendFunc.BlendFlags := 0; blendFunc.SourceConstantAlpha := 255; blendFunc.AlphaFormat := AC_SRC_ALPHA; // 直接将裁剪区域绘制到背景 AlphaBlend(background.Canvas.Handle, destRect.Left, destRect.Top, destRect.Width, destRect.Height, frontBmp.Canvas.Handle, srcRect.Left, srcRect.Top, srcRect.Width, srcRect.Height, blendFunc); // 绘制最终结果到窗体 frmMain.Canvas.Draw(0, 0, background); finally frontBmp.Free; end; finally frontsigns.Free; end; finally background.Free; end; end;
3. 第三方库选择
如果不想手动优化底层逻辑,可选用成熟的图形库:
- Graphics32:Delphi高性能图形处理库,支持带Alpha位图的快速裁剪、绘制,底层用内存拷贝替代逐像素操作,性能远超原生VCL。
- ImageEn:功能全面的图形库,对PNG的裁剪、绘制做了大量优化,API简洁易用,适合快速开发。
内容的提问来源于stack exchange,提问作者brane
相关产品推荐
相关产品推荐

