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);
Мне потребовались все выходные, чтобы разобраться в этом сам, надеюсь, это поможет вам или кому-то еще.