Delphi, Direct2D, TBitmap и Transparency

Я изо всех сил пытаюсь нарисовать 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);

Мое понимание теперь приводит меня к ID2D1Bitmap а также IWICBitmap интерфейсы, поэтому я могу попытаться создать ID2D1Bitmap от TBitmap используя следующий код (и предполагая, что формат пикселя копируется):

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);

И результат все еще непрозрачный точечный рисунок.

Итак, последнее, на что я обратил внимание, это PixelFormat ID2D1RenderTarget, которая является основной целью рендеринга 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 игнорирует альфа.

2 ответа

Реальная проблема не в методах, которые вы вызываете, а в том факте, что в приложении VCL по умолчанию TBitmap использует 24-битный формат пикселей RGB, в котором нет альфа-канала, необходимого для альфа-прозрачности.

Если вы хотите использовать альфа-прозрачность с TBitmap, вам сначала нужно установить его формат пикселей pf32bit,

/questions/11743417/podderzhivaet-li-delphi-tbitmap-alfa-kanal/11743430#11743430

Также не забудьте установить альфа-канал на 0 для каждого пикселя, который вы хотите, чтобы он был прозрачным.

Вы видите, что Direct2D не поддерживает ту же прозрачность, что и в VCL, где вы можете просто установить прозрачный цвет, и каждый пиксель этого определенного цвета просто игнорируется.

Если вы посмотрите на источник 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;

Затем вам нужно подготовить альфа-канал для каждого пикселя. В вашем случае красный цвет прозрачный, поэтому вы должны сделать что-то вроде этого:

      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);

Мне потребовались все выходные, чтобы разобраться в этом сам, надеюсь, это поможет вам или кому-то еще.

Другие вопросы по тегам