有什么方法可以加快位图 canvas 的绘制速度?
Any way to speed up drawing on bitmap canvas?
我的应用程序的一部分是绘制图形表单草稿,主要是用户输入表单,带有许多编辑框。我在位图上绘制并另存为 PNG,这是报告过程的所有部分,因此没有在申请表上绘制任何内容。
我绘制所有控件、编辑框、单选按钮、复选框...
画的最长的部分当然是画编辑框了,因为编辑框的数量太多了。我有一个典型的过程,在大约 7 万个表单上绘制 100 万个编辑框。
我画了一个很简单的编辑框:
- 基本矩形,笔宽 = 1
- 添加左侧和顶部附加行,以获得缩进控制的感觉。
- 根据需要添加文本
我附上了简单编辑控件的屏幕截图,放大后,可以看到额外的左线和顶线。
这里是DrawEdit的代码,绘制1M个编辑框,耗时20s。 有没有办法加快这个过程?
procedure DrawEdit(myCanvas:TCanvas; vLeft, vTop, vWidth, vHeight: integer; const vText: string; vCenterText:boolean=false);
begin
// basic rectangle
pPoint.x := vLeft; pPoint.Y := vTop;
myCanvas.Pen.Width := 1;
myCanvas.PenPos := pPoint;
myCanvas.Pen.Color := [=11=]99A8AC;
myCanvas.LineTo(vLeft + vWidth, vTop);
myCanvas.LineTo(vLeft + vWidth, vTop + 1);
myCanvas.Pen.Color := [=11=]E2EFF1;
myCanvas.LineTo(vLeft + vWidth, vTop + vHeight - 1);
myCanvas.LineTo(vLeft, vTop + vHeight - 1);
myCanvas.Pen.Color := [=11=]99A8AC;
myCanvas.LineTo(vLeft, vTop);
// again top border
pPoint.x := vLeft + 1; pPoint.Y := vTop + 1;
myCanvas.PenPos := ppoint;
myCanvas.Pen.Color := [=11=]646F71;
myCanvas.LineTo(vLeft + vWidth, vTop + 1);
// again left border
ppoint.x := vLeft + 1; ppoint.Y := vTop + 1;
myCanvas.PenPos := ppoint;
myCanvas.Pen.Color := [=11=]646F71;
myCanvas.LineTo(vLeft + 1, vTop + vHeight - 1);
if vText<>'' then
begin
// clear area for text - white background
myCanvas.Font.Color := clblack;
myCanvas.Brush.Color := clWhite;
rRect.Left := vLeft + 2;
rRect.Top := vTop + 2;
rRect.Right := vLeft + myCanvas.TextWidth(vText);
rRect.Bottom := vTop + myCanvas.TextHeight(vText);
myCanvas.FillRect(rRect);
If Not vCenterText Then
Winapi.Windows.TextOut(myCanvas.Handle, vLeft + 4, vTop + 2, PChar(vText), Length(vText))
else
Winapi.Windows.TextOut(myCanvas.Handle, vLeft + (vWidth div 2) - (myCanvas.TextWidth(vText) div 2), vTop + (vHeight div 2) - (myCanvas.TextHeight(vText) div 2), PChar(vText), Length(vText));
end;
end;
这是我的测试 use.The 测试并不理想,因为真实的编辑框大小不同,但这是我测试不同优化选项的测试。
procedure TForm1.Button3Click(Sender: TObject);
var
i, t1, t2: integer;
myBitmap: TBitmap;
begin
myBitmap := TBitmap.Create;
try
myBitmap.SetSize(500, 500);
myBitmap.PixelFormat := pf24bit;
t1 := GetTickCount;
for i := 1 to 1000000 do
DrawEdit(myBitmap.Canvas, 10, 10, 100, 50, 'Edit box', true);
t2 := GetTickCount;
finally
myBitmap.Free;
end;
button3.Caption := inttostr(t2 - t1);
end;
GDI 可以高效地绘制到设备,但是,对于内存位图工作,它会带来很大的开销。
你最好避开 GDI 层,直接对内存中的光栅图像执行此操作。我为此推荐 graphics32 库。切换到那个应该会产生非常显着的性能提升。
您可能做的另一件事是将工作拆分为多个任务并利用多线程。您想要绘制不同的位图,然后在最后拼接在一起。
我使用 Line
从 Graphics32 在 TBitmap32 上绘图,结果比在 TBitmap.Canvas 上绘图快 3 倍:
procedure DrawEdit32(BM32: TBitmap32; vLeft, vTop, vWidth, vHeight: integer; const vText: string; vCenterText: boolean = false);
begin
// basic rectangle
BM32.Line(vLeft, vTop, vWidth, vTop, [=10=]99A8AC);
BM32.Line(vWidth, vTop, vWidth, vTop + vHeight, [=10=]E2EFF1);
BM32.Line(vWidth, vTop + vHeight, vLeft, vTop + vHeight, [=10=]E2EFF1);
BM32.Line(vLeft, vTop + vHeight, vLeft, vTop, [=10=]99A8AC);
// again top border
BM32.Line(vLeft + 1, vTop + 1, vWidth, vTop + 1, [=10=]646F71);
// again left border
BM32.Line(vLeft + 1, vTop + 1, vLeft + 1, vTop + vHeight, [=10=]646F71);
if vText <> '' then
begin
// clear area for text - white background
BM32.Font.Color := clblack;
rRect.Left := vLeft + 2;
rRect.Top := vTop + 2;
rRect.Right := vLeft + BM32.TextWidth(vText);
rRect.Bottom := vTop + BM32.TextHeight(vText);
BM32.FillRect(rRect.Left,rRect.Top,rRect.Right,rRect.Bottom,clWhite);
if not vCenterText then
Winapi.Windows.TextOut(BM32.Handle, vLeft + 4, vTop + 2, PChar(vText), Length(vText))
else
Winapi.Windows.TextOut(BM32.Handle, vLeft + (vWidth div 2) - (BM32.TextWidth(vText) div 2), vTop + (vHeight div 2) - (BM32.TextHeight(vText) div 2), PChar(vText), Length(vText));
end;
end;
我的应用程序的一部分是绘制图形表单草稿,主要是用户输入表单,带有许多编辑框。我在位图上绘制并另存为 PNG,这是报告过程的所有部分,因此没有在申请表上绘制任何内容。 我绘制所有控件、编辑框、单选按钮、复选框...
画的最长的部分当然是画编辑框了,因为编辑框的数量太多了。我有一个典型的过程,在大约 7 万个表单上绘制 100 万个编辑框。
我画了一个很简单的编辑框:
- 基本矩形,笔宽 = 1
- 添加左侧和顶部附加行,以获得缩进控制的感觉。
- 根据需要添加文本
我附上了简单编辑控件的屏幕截图,放大后,可以看到额外的左线和顶线。
这里是DrawEdit的代码,绘制1M个编辑框,耗时20s。 有没有办法加快这个过程?
procedure DrawEdit(myCanvas:TCanvas; vLeft, vTop, vWidth, vHeight: integer; const vText: string; vCenterText:boolean=false);
begin
// basic rectangle
pPoint.x := vLeft; pPoint.Y := vTop;
myCanvas.Pen.Width := 1;
myCanvas.PenPos := pPoint;
myCanvas.Pen.Color := [=11=]99A8AC;
myCanvas.LineTo(vLeft + vWidth, vTop);
myCanvas.LineTo(vLeft + vWidth, vTop + 1);
myCanvas.Pen.Color := [=11=]E2EFF1;
myCanvas.LineTo(vLeft + vWidth, vTop + vHeight - 1);
myCanvas.LineTo(vLeft, vTop + vHeight - 1);
myCanvas.Pen.Color := [=11=]99A8AC;
myCanvas.LineTo(vLeft, vTop);
// again top border
pPoint.x := vLeft + 1; pPoint.Y := vTop + 1;
myCanvas.PenPos := ppoint;
myCanvas.Pen.Color := [=11=]646F71;
myCanvas.LineTo(vLeft + vWidth, vTop + 1);
// again left border
ppoint.x := vLeft + 1; ppoint.Y := vTop + 1;
myCanvas.PenPos := ppoint;
myCanvas.Pen.Color := [=11=]646F71;
myCanvas.LineTo(vLeft + 1, vTop + vHeight - 1);
if vText<>'' then
begin
// clear area for text - white background
myCanvas.Font.Color := clblack;
myCanvas.Brush.Color := clWhite;
rRect.Left := vLeft + 2;
rRect.Top := vTop + 2;
rRect.Right := vLeft + myCanvas.TextWidth(vText);
rRect.Bottom := vTop + myCanvas.TextHeight(vText);
myCanvas.FillRect(rRect);
If Not vCenterText Then
Winapi.Windows.TextOut(myCanvas.Handle, vLeft + 4, vTop + 2, PChar(vText), Length(vText))
else
Winapi.Windows.TextOut(myCanvas.Handle, vLeft + (vWidth div 2) - (myCanvas.TextWidth(vText) div 2), vTop + (vHeight div 2) - (myCanvas.TextHeight(vText) div 2), PChar(vText), Length(vText));
end;
end;
这是我的测试 use.The 测试并不理想,因为真实的编辑框大小不同,但这是我测试不同优化选项的测试。
procedure TForm1.Button3Click(Sender: TObject);
var
i, t1, t2: integer;
myBitmap: TBitmap;
begin
myBitmap := TBitmap.Create;
try
myBitmap.SetSize(500, 500);
myBitmap.PixelFormat := pf24bit;
t1 := GetTickCount;
for i := 1 to 1000000 do
DrawEdit(myBitmap.Canvas, 10, 10, 100, 50, 'Edit box', true);
t2 := GetTickCount;
finally
myBitmap.Free;
end;
button3.Caption := inttostr(t2 - t1);
end;
GDI 可以高效地绘制到设备,但是,对于内存位图工作,它会带来很大的开销。
你最好避开 GDI 层,直接对内存中的光栅图像执行此操作。我为此推荐 graphics32 库。切换到那个应该会产生非常显着的性能提升。
您可能做的另一件事是将工作拆分为多个任务并利用多线程。您想要绘制不同的位图,然后在最后拼接在一起。
我使用 Line
从 Graphics32 在 TBitmap32 上绘图,结果比在 TBitmap.Canvas 上绘图快 3 倍:
procedure DrawEdit32(BM32: TBitmap32; vLeft, vTop, vWidth, vHeight: integer; const vText: string; vCenterText: boolean = false);
begin
// basic rectangle
BM32.Line(vLeft, vTop, vWidth, vTop, [=10=]99A8AC);
BM32.Line(vWidth, vTop, vWidth, vTop + vHeight, [=10=]E2EFF1);
BM32.Line(vWidth, vTop + vHeight, vLeft, vTop + vHeight, [=10=]E2EFF1);
BM32.Line(vLeft, vTop + vHeight, vLeft, vTop, [=10=]99A8AC);
// again top border
BM32.Line(vLeft + 1, vTop + 1, vWidth, vTop + 1, [=10=]646F71);
// again left border
BM32.Line(vLeft + 1, vTop + 1, vLeft + 1, vTop + vHeight, [=10=]646F71);
if vText <> '' then
begin
// clear area for text - white background
BM32.Font.Color := clblack;
rRect.Left := vLeft + 2;
rRect.Top := vTop + 2;
rRect.Right := vLeft + BM32.TextWidth(vText);
rRect.Bottom := vTop + BM32.TextHeight(vText);
BM32.FillRect(rRect.Left,rRect.Top,rRect.Right,rRect.Bottom,clWhite);
if not vCenterText then
Winapi.Windows.TextOut(BM32.Handle, vLeft + 4, vTop + 2, PChar(vText), Length(vText))
else
Winapi.Windows.TextOut(BM32.Handle, vLeft + (vWidth div 2) - (BM32.TextWidth(vText) div 2), vTop + (vHeight div 2) - (BM32.TextHeight(vText) div 2), PChar(vText), Length(vText));
end;
end;