【问题标题】:Why am I losing transparency when calling BitBlt or CopyRect?为什么我在调用 BitBlt 或 CopyRect 时会失去透明度?
【发布时间】:2014-10-24 00:04:42
【问题描述】:

问题

我正在尝试将 32x32 瓷砖从 TBitmap 复制到 TPaintbox 这是我的地图编辑器,但我似乎无法让透明度正常工作。

见下图:

注意:出于演示和测试的目的,我在 TPaintbox 下方放置了一个 TImage,这将有助于查看透明度是否有效。

如您所见,常规图块绘制正确,但应该透明的图块使用白色背景绘制。

我现在正在使用适当的类来管理我的地图和图块,以下是我尝试过的两种绘图方式:

复制矩形:

procedure TMap.DrawTile(Tileset: TBitmap; MapX, MapY, TileX, TileY: Integer;
  MapCanvas: TCanvas);
begin
  if TileIsFree(MapX, MapY) then
  begin
    MapCanvas.CopyRect(
      Rect(MapX, MapY, MapX + fTileWidth, MapY + fTileHeight),
      Tileset.Canvas,
      Rect(TileX, TileY, TileX + fTileWidth, TileY + fTileHeight));
  end;
end;

BitBlt

procedure TMap.DrawTile(Tileset: TBitmap; MapX, MapY, TileX, TileY: Integer;
  MapCanvas: TCanvas);
begin
  if TileIsFree(MapX, MapY) then
  begin
    BitBlt(
      MapCanvas.Handle,
      MapX,
      MapY,
      fTileWidth,
      fTileHeight,
      Tileset.Canvas.Handle,
      TileX,
      TileY,
      SRCCOPY);
  end;
end;

我已经尝试使用位图和 png 图像格式来制作瓦片集(屏幕截图上的左图)。 bitmap 和 png 之间的唯一区别是 CopyRect 在它是 png 时甚至难以绘制几个图块,但 BitBlt 设法绘制而没有任何明显的缺点。

无论如何,我如何将TBitmap 的一部分复制/绘制到TPaintbox 上而不会失去透明度,或者在我的情况下也不会复制白色背景?

更新 1

从下面的一些 cmets 开始,我尝试调用 AlphaBlend 函数,但这仍然会留下不良结果(注意透明区域周围的蓝色):

procedure TMap.DrawTile(Tileset: Graphics.TBitmap; MapX, MapY, TileX, TileY: Integer;
  MapCanvas: TCanvas);
var
  BlendFn: TBlendFunction;
begin
  if TileIsFree(MapX, MapY) then
  begin
    BlendFn.BlendOp := AC_SRC_OVER;
    BlendFn.BlendFlags := 0;
    BlendFn.SourceConstantAlpha := 255;
    BlendFn.AlphaFormat := AC_SRC_ALPHA;

    AlphaBlend(
      MapCanvas.Handle,
      MapX,
      MapY,
      fTileWidth,
      fTileHeight,
      Tileset.Canvas.Handle,
      TileX,
      TileY,
      fTileWidth,
      fTileHeight,
      BlendFn);
  end;
end;

谢谢。

【问题讨论】:

  • BitBlt、StretchBlt (CopyRect) 不支持透明度。使用 Canvas.Draw。
  • @Blobby:在 Delphi 中使用Windows 单元时也会出现同样的问题,因为Windows.TBitmap 记录与Graphics.TBitmap 类不同。
  • @Blobby - 是的,使用临时位图可能会影响性能。
  • 我已成功使用AlphaBlend() 进行类似绘图。这也需要一个 BlendFunction 参数。在 MSDN 中有说明。

标签: delphi lazarus


【解决方案1】:

有 3 种流行的方式来处理透明位图,前两种使用标准 Delphi 工具,第三种需要第三方库:

如果您使用两种标准方法之一,请不要使用 BitBlt 或 CopyRect。使用透明图像持有者的 Draw 方法在目标画布上绘制。

  1. 将透明位图保存在 TImageList 中,并使用 TImageList.Draw 直接在目标画布上绘制(不要在中间位图上绘制,因为这里会失去透明度)。要在设计时将图像添加到图像列表,请右键单击并选择图像列表编辑器。列表中的图像可以是位图、图标、PNG、GIF 和 JPEG 图像:TImage 支持的任何图像类型。 ImageLists 还支持 32 位格式,因此 alpha 混合位图和 PNG 文件可以正常工作。您还可以在运行时加载图像。如果您的位图以非透明形式存储但有透明颜色,则可以使用 TImageList.AddMasked(Bitmap: TBitmap; MaskColor: TColor) 方法。您可以在第二个参数中自己传递透明颜色,也可以通过 clDefault 让图像列表采用左下角像素的颜色。
  2. 将您的图像保存在 PNG 文件或资源中并将它们加载到 Vcl.Imaging.pngimage.TpngImage 中,然后调用 TpngImage.Draw 将您的 PNG 直接绘制在目标画布上。如上所述,不要在中间位图上绘画,因为您会在这里失去透明度。
  3. 使用第三方库 GR32 中的 TBitmap32。在这种情况下,请勿使用带有透明图像的 TBitmap32 直接在 HDC、Canvas 或 TBitmap 上绘制。使用 dmBlend 和 DrawTo 或 BlockTransfer() 在另一个 TBitmap32 上绘图。例如,要在 TBitmap 上透明绘制,创建中间缓存 TBitmap32: (1) 将图像从 TBitmap 复制到缓存 TBitmap32; (2) 使用 DrawTo 或 BlockTransfer() 将透明图像应用到缓存 TBitmap32,避免使用 Canvas 或 HDC 混合两个图像,因为它们会丢失 alpha 层信息; (3) 将图像从缓存 TBitmap32 复制回您的 TBitmap。

【讨论】:

    猜你喜欢
    • 2012-06-17
    • 2010-10-19
    • 2017-02-14
    • 1970-01-01
    • 2010-12-10
    • 1970-01-01
    • 2013-09-13
    • 2016-11-12
    • 1970-01-01
    相关资源
    最近更新 更多