Why am I losing transparency when calling BitBlt or CopyRect?

空扰寡人 提交于 2019-12-22 06:08:43

问题


Problem

I am trying to copy 32x32 tiles from a TBitmap into a TPaintbox which is my map editor, but I cannot seem to get the transparency working correctly.

See the image below:

Note: For the purpose of the demo and testing, I have placed a TImage underneath the TPaintbox which would help see if the transparency is working or not.

As you can see, regular tiles draw correctly, but the tiles that should be transparent are drawn with a white background.

I am now using proper classes to manage my maps and tiles, and below is two ways I have tried drawing:

CopyRect:

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;

I have tried using bitmap and png image formats for the tileset (left image on the screenshot). The only difference between bitmap and png is that CopyRect struggles to draw even a few tiles when it is a png, but BitBlt manages to draw without any obvious drawbacks.

Anyway, how do I copy/draw part of a TBitmap onto a TPaintbox without losing transparency, or in my case without also copying the white background?

Update 1

Following on from some of the comments below I have tried calling the AlphaBlend function but this still leaves undesirable results (note the blue colors around the transparent areas):

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;

Thanks.


回答1:


There are 3 popular ways to work with transparent bitmaps, first two use standard Delphi tools and the third one requires a third-party library:

If you use one of the two standard methods, don’t use BitBlt or CopyRect. Use the Draw method of the transparent image holder to draw on destination canvas.

  1. Keep your transparent bitmaps in a TImageList and use TImageList.Draw to paint directly on destination canvas (don’t paint on intermediary bitmaps since you will lose transparency here). To add images to an image list at design time, right click and choose Image List Editor. Images in the list may be bitmaps, icons, PNG, GIF and JPEG images: any image type that TImage supports. ImageLists also support 32-bit format, so alpha blended bitmaps and PNG files work properly. You can also load images at run time. If your bitmaps are stored in a non-transparent form but there is a transparent color, you can use the TImageList.AddMasked(Bitmap: TBitmap; MaskColor: TColor) method. You can either pass the transparent color yourself in the second parameter or clDefault to let the imagelist take the bottom-left pixel's color.
  2. Keep your images in PNG files or resources and load them into a Vcl.Imaging.pngimage.TpngImage, then call TpngImage.Draw to paint your PNG directly on destination canvas. As above, don’t paint on intermediary bitmaps since you will lose transparency here.
  3. Use TBitmap32 from GR32, a third-party library. In this case, Do not use TBitmap32 with transparent images to draw directly on HDC, Canvas or TBitmap. Use dmBlend and DrawTo or BlockTransfer() to draw on another TBitmap32. For example, to draw transparently on a TBitmap, create an intermediary cache TBitmap32: (1) Copy the image from TBitmap to the cache TBitmap32; (2) Apply your transparent image to the cache TBitmap32 using DrawTo or BlockTransfer(), avoid using Canvas or HDC to mix two images because they lose alpha layer information; (3) Copy the image back from the cache TBitmap32 to your TBitmap.


来源:https://stackoverflow.com/questions/25577387/why-am-i-losing-transparency-when-calling-bitblt-or-copyrect

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!