【问题标题】:Delphi, Direct2D, TBitmap and TransparencyDelphi、Direct2D、TBitmap 和透明度
【发布时间】:2016-11-22 03:43:39
【问题描述】:

我很难在不丢失透明度的情况下将具有透明度的 TBitmap 绘制到 TDirect2DCanvas 上。

创建了一个TBitmap 作为我的绘图操作的后台缓冲区,如下所示:

bmp := TBitmap.Create;
bmp.Canvas.Brush.Handle := 0;
bmp.SetSize(100, 100);
bmp.Canvas.Brush.Color := clRed;
bmp.Transparent := true;
bmp.TransparentColor := clRed;
bmp.Canvas.Rectangle(bmp.Canvas.ClipRect);
bmp.Canvas.Pen.Color := clGreen;
bmp.Canvas.Ellipse(bmp.Canvas.ClipRect);

然后我需要将它绘制到我的TDirect2DCanvas 上,但是下面会绘制TBitmap 但会删除所有透明度 - 背景颜色绘制为红色,而如果我只是绘制到TForm.Canvas 上,则背景是透明的.

// Drawing onto the TDirect2DCanvas results in a red background
AEventArgs.Canvas.Draw(0, 0, bmp);

// Drawing onto the TForm.Canvas gives the correct result
Self.Canvas.Draw(0, 0, bmp);

我的理解现在将我引向ID2D1BitmapIWICBitmap 接口,因此,我可以尝试使用以下代码从TBitmap 创建一个ID2D1Bitmap(并且假设像素格式被复制):

var
    bmp : TBitmap;
    temp : ID2D1Bitmap;
begin
    // Code to initialize the TBitmap goes here (from above)

    // Create an ID2D1Bitmap from a TBitmap
    temp := AEventArgs.Canvas.CreateBitmap(bmp);

    // Draw the ID2D1Bitmap onto the TDirect2DCanvas
    AEventArgs.Canvas.RenderTarget.DrawBitmap(temp);

现在我有了ID2D1Bitmap,结果还是一样的——没有透明度的红色背景。我猜 Direct2D 方面使用不同的透明度方法是完全可行的,但查看 ID2D1Bitmap 的属性并没有提供任何线索。

我的下一个猜测是去IWICBitmap接口。

最后,我的问题是:有没有我从上面遗漏的更直接或更明显的事情可以让透明的TBitmap 被绘制到TDirect2DCanvas 表面上?或者为了保持透明度,所有这些痛苦都是必要的?

更新

好的,所以在进行了更多挖掘之后,我现在可以将TBitmap 转换为IWICBitmap,然后转换为ID2D1Bitmap,但是问题仍然存在-TBitmap 中存在的透明度是渲染到 TDirect2DCanvas 时未复制。

// Create the IWICBitmap from the TBitmap
GetWICFactory.CreateBitmapFromHBITMAP(bmp.Handle, bmp.Palette, WICBitmapUsePremultipliedAlpha, wic);
wic.GetPixelFormat(pif);

// The PixelFormat is correct as `GUID_WICPixelFormat32bppPBGRA` which is
// B8G8R8A8_UNORM and PREMULTIPLIED

// Create the IWICFormatConverter
GetWICFactory.CreateFormatConverter(fc);
fc.Initialize(wic, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, nil, 0.0, WICBitmapPaletteTypeCustom);

// Now, create the ID2D1Bitmap
AEventArgs.Canvas.RenderTarget.CreateBitmapFromWicBitmap(fc, nil, temp);
temp.GetPixelFormat(fmt);

// Here, PixelFormat is correct matching the PixelFormat from the IWICBitmap

// Draw the bitmap to the Canvas
AEventArgs.Canvas.RenderTarget.DrawBitmap(temp);

结果仍然是不透明的位图。

所以我最后研究的是 ID2D1RenderTarget 的 PixelFormat,它是 TDirect2DCanvas 的底层渲染目标。

// Create the canvas
fCanvas := TDirect2DCanvas.Create(Self.Handle);
fCanvas.RenderTarget.GetPixelFormat(pf); 

// This gives me a PixelFormat of
// B8G8R8A8_UNORM but D2D1_ALPHA_MODE_IGNORE 

所以我猜真正的问题在于ID2D1RenderTarget PixelFormat 忽略了 alpha。

【问题讨论】:

  • > ".. TBitmap 中存在的透明度.." > 这就是你一直以来的错误。该位图没有透明度。 VCL 使用您通过 TBitmap 的属性提供的信息透明地绘制它(参见“图形”中的TransparentStretchBlt)。

标签: delphi transparent direct2d wic


【解决方案1】:

真正的问题不在于您调用的方法,而是在 VCL 应用程序中默认情况下 TBitmap 使用 24 位 RGB 像素格式的剪切事实,该格式没有 alpha 透明度所需的 alpha 通道。

如果您想在 TBitmap 中使用 alpha 透明度,您首先需要将其像素格式设置为 pf32bit

https://stackoverflow.com/a/4680460/3636228

此外,不要忘记将 Alpha 通道设置为 0,以便让每个像素透明。

您会看到 Direct2D 不支持与在 VCL 中使用的透明度相同的透明度,在 VCL 中您可以简单地设置透明颜色,并且该特定颜色的每个像素都会被简单地忽略。

【讨论】:

  • @SilverWarrior 感谢您的 cmets。关于如何在保持透明度的同时在 VCL TBitmap 和 Direct2D 之间切换的任何指示?我已经看过使用与 GDI 兼容的渲染目标,但即使在 TBitmap 和渲染目标上都正确设置了 alpha,这仍然不起作用 - 它仍然会被忽略。周围似乎没有太多有用的信息。谢谢
  • 也许您应该尝试使用 FMX.Graphics.TBitmap 而不是常规的 VCL TBitmap,正如我在另一个问题中针对不同目的所建议的那样,您可以在此处找到:stackoverflow.com/a/38551815/3636228 请注意,我从未尝试过this 或 seey anboddy else 会这样做,但基于 FireMonkey 使用 DirectX 在 Windows 上进行渲染的事实,它可能实际上可以工作。我回家后会做一些测试。
【解决方案2】:

如果你看一下 TDirect2DCanvas.CreateBitmap 的源码,你会看到:

  ...
  if (Bitmap.PixelFormat <> pf32bit) or (Bitmap.AlphaFormat = afIgnored) then
    BitmapProperties.pixelFormat.alphaMode := D2D1_ALPHA_MODE_IGNORE
  else
    BitmapProperties.pixelFormat.alphaMode := D2D1_ALPHA_MODE_PREMULTIPLIED;

所以要让它工作,你必须匹配条件:

    bmp.PixelFormat := pf32bit;
    bmp.AlphaFormat := TAlphaFormat.afPremultiplied;

然后你必须准备每个像素的 alpha 通道。在你的情况下,红色是透明的,所以你应该这样做:

for y := 0 to bmp.Height - 1 do begin
  Line := bmp.Scanline[y];
  for x := 0 to bmp.Width - 1 do begin
    if (line[x].r =255) and (line[x].g = 0) and (line[x].b = 0) then
    Line[x].A := 0
  else
    Line[x].A := 255;
end;

然后它来了:

temp := AEventArgs.Canvas.CreateBitmap(BMP);
AEventArgs.Canvas.RenderTarget.DrawBitmap(temp);

我自己花了整个周末才弄明白,希望对你或其他人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-14
    • 1970-01-01
    • 1970-01-01
    • 2013-02-21
    • 1970-01-01
    相关资源
    最近更新 更多