如何在Bitmap上渲染棋盘网格?

时间:2012-07-17 19:52:36

标签: delphi delphi-xe

我正在另一个“下方”图像上显示透明图像。

在这种情况下,底部(实心)图像是棋盘网格,顶部图像是狮子(透明):

enter image description here

enter image description here = enter image description here

原因是要更好地显示透明度区域,因为通常您不会看到哪些区域是透明的。

问题是,位图的尺寸可以是任何尺寸,因此网格也需要与位图的尺寸相同。

如果你愿意,一个肮脏的方法是创建一个更大版本的棋盘网格,大小如2000x2000,那么根据你正在使用的位图的大小,你可以调整网格的画布以匹配。这并不理想,因为它意味着将大棋盘网格位图与您的应用程序一起存储,然后它意味着调整大小,这可能无法根据宽高比等给出正确的结果。

我认为正确的方法是以编程方式呈现棋盘网格,例如:

procedure RenderGrid(Source: TBitmap; Height, Width: Integer;
  Size: Integer; Color1, Color2: TColor);
begin

end;

这将允许自定义具有不同大小和颜色的网格,而不用担心存储大型棋盘网格位图的开销并且不得不调整它的大小。

但是我不确定如何将网格绘制到位图上?我有一个想法是你需要遍历位图的每个交替行并以这种方式着色吗?我不确定。

这涉及我不擅长的数学和计算。如果您能够通过最有效的方式在位图上渲染网格,我将不胜感激。

3 个答案:

答案 0 :(得分:5)

procedure RenderGrid(Source: TBitmap; Height, Width: Integer;
  Size: Integer; Color1, Color2: TColor);
var
  y: Integer;
  x: Integer;
begin
  Source.SetSize(Width, Height);
  for y := 0 to Height div Size do
    for x := 0 to Width div Size do
    begin
      if Odd(x) xor Odd(y) then
        Source.Canvas.Brush.Color := Color1
      else
        Source.Canvas.Brush.Color := Color2;
      Source.Canvas.FillRect(Rect(x*Size, y*Size, (x+1)*Size, (y+1)*Size));
    end;
end;

答案 1 :(得分:3)

曾几何时,我profiled this specific need。考虑到您的RenderGrid签名,很可能在绘制位图后绘制Bitmap参数的图像。然后通过在Color1中绘制整个位图来获得最佳性能,并仅绘制Color2的方块:

procedure RenderGrid(Target: TBitmap; Height, Width: Integer; Size: Integer;
  Color1, Color2: TColor);
var
  Col: Integer;
  Row: Integer;
begin
  Target.SetSize(Width, Height)
  Target.Canvas.Brush.Color := Color1;
  Target.Canvas.FillRect(Rect(0, 0, Width, Height));
  Target.Canvas.Brush.Color := Color2;
  for Col := 0 to Width div Size do
    for Row := 0 to Height div Size do
      if Odd(Col + Row) then
        Target.Canvas.FillRect(Bounds(Col * Size, Row * Size, Size, Size));
end;

更新

但是,由于您正在谈论大型位图,下面显示的例程甚至会快20%。它创建一个只有4个正方形的小位图,比如一个2 x 2的棋盘,并让目标的画笔属性自动展开。 *)

procedure RenderGrid(Target: TBitmap; Height, Width: Integer; Size: Integer;
  Color1, Color2: TColor);
var
  Tmp: TBitmap;
begin
  Tmp := TBitmap.Create;
  try
    Tmp.Canvas.Brush.Color := Color1;
    Tmp.Width := 2 * Size;
    Tmp.Height := 2 * Size;
    Tmp.Canvas.Brush.Color := Color2;
    Tmp.Canvas.FillRect(Rect(0, 0, Size, Size));
    Tmp.Canvas.FillRect(Bounds(Size, Size, Size, Size));
    Target.Canvas.Brush.Bitmap := Tmp;
    if Target.Width * Target.Height = 0 then
      Target.SetSize(Width, Height)
    else
    begin
      Target.SetSize(Width, Height)
      Target.Canvas.FillRect(Rect(0, 0, Width, Height));
    end;
  finally
    Tmp.Free;
  end;
end;

进一步优化:缓存此小位图(Tmp),并在其大小未更改时重复使用。

*)另见:How to color a bitmap without calling FillRect()?

答案 2 :(得分:-1)

使用这种方法可以获得更好的性能。只是不要传递CellSize = 0.

// Color1, Color2 in RRGGBB format (i.e. Red = $00FF0000)
procedure RenderGrid(Source: TBitmap; CellSize: Integer; Color1, Color2: TColorRef);
var
  I, J: Integer;
  Pixel: ^TColorRef;
  UseColor1: Boolean;
begin
  Source.PixelFormat := pf32bit;
  Pixel := Source.ScanLine[Source.Height - 1];
  for I := 0 to Source.Height - 1 do
  begin
    UseColor1 := (I div CellSize) mod 2 = 0;
    for J := 0 to Source.Width - 1 do
    begin
      if J mod CellSize = 0 then UseColor1 := not UseColor1;

      if UseColor1 then
        Pixel^ := Color1
      else
        Pixel^ := Color2;
      Inc(Pixel);
    end;
  end;
end;