Прозрачное изображение PNG, загруженное из файла ресурсов, измененное с помощью Grapics32 и нарисованное на холсте - PullRequest
4 голосов
/ 25 января 2020

Мне нужна небольшая помощь ...

У меня есть прозрачное изображение PNG в ресурсах моего приложения. До сих пор я загружал его в TPngImage и рисовал на экране Canvas.Draw(X, Y, PngImage);. И это было нарисовано прозрачно. Теперь я обновил свое приложение до версии DpiAware, и мне нужно масштабировать все изображения. Мне нужен качественный ресэмплер, и я решил использовать Graphics32. Мне удалось сделать повторную выборку, но я не знаю, как сохранить прозрачность ... Я пробую все, о чем я думаю ... Результат следующего кода - изображение, нарисованное черным цветом в прозрачной области ... .

Foto32, Buff: TBitmap32;
FotoPng: TPngImage;

constructor TForm.Create(AOwner: TComponent);
const BkgHeight = 380;
var Res: TKernelResampler;
    SRect, DRect: TRect;
    ImgWidth: Integer;
begin
 inherited;
 Buff:= TBitmap32.Create;
 Res:= TKernelResampler.Create;
 Res.Kernel:= TLanczosKernel.Create;

 FotoPng:= TPngImage.Create;
 FotoPng.Transparent:= True;
 FotoPng.TransparentColor:= clBlack;
 FotoPng.LoadFromResourceName(HInstance, 'BKG_FOTO');
 Foto32:= TBitmap32.Create;
 Foto32.DrawMode:= dmBlend;
 Foto32.CombineMode:= cmMerge;
 Foto32.OuterColor:= clBlack;
 Foto32.Canvas.Brush.Style:= bsClear;
 Foto32.SetSize(FotoPng.Width, FotoPng.Height);
 FotoPng.Draw(Foto32.Canvas, Rect(0, 0, FotoPng.Width, FotoPng.Height));

 ImgWidth:= Round(Real(Foto32.Width / Foto32.Height) * BkgHeight);
 SRect:= Rect(0, 0, Foto32.Width, Foto32.Height);
 Buff.DrawMode:= dmBlend;
 Buff.CombineMode:= cmMerge;
 Buff.OuterColor:= clBlack;
 Buff.Canvas.Brush.Style:= bsClear;
 Buff.SetSize(Scale(ImgWidth), Scale(BkgHeight));
 DRect:= Rect(0, 0, Buff.Width, Buff.Height);
 Res.Resample(Buff, DRect, DRect, Foto32, SRect, dmTransparent {dmBlend}, nil);
end;

procedure TForm.Paint;
begin
 // ....
 Buff.DrawTo(Canvas.Handle, X, Y);
end;

И это мое прозрачное изображение PNG, скомпилированное в ресурсы: https://postimg.cc/3yy3wrJB

Я нашел здесь аналогичный вопрос , но Я не использую изображение с TImage, я рисую его прямо на холсте. И в одном ответе Дэвид говорит:

В любом случае, если бы это было так, я бы объединил поддержку прозрачности TImage с возможностью повторной выборки TBitmap32, чтобы создать решение таким образом. Сохраните исходное изображение в экземпляре TBitmap32. Всякий раз, когда вам нужно загрузить его в компонент TImage, например, при изменении размера, используйте TBitmap32 для выполнения изменения размера в памяти и загрузки изображения с измененным размером.

Это именно то, что Я пытаюсь сделать, но я не знаю, почему прозрачность не работает. Любые идеи ?

1 Ответ

1 голос
/ 05 февраля 2020

Похоже, ваша проблема связана с отображением буфера на экран. Bitmap32 использует StretchDIBits для рисования, которое игнорирует альфа-канал.

Вы можете использовать функцию AlphaBlend, чтобы нарисовать ваше изображение:

procedure TForm1.FormPaint(Sender: TObject);
var
  BF: TBlendFunction;
begin
  BF.BlendOp := AC_SRC_OVER;
  BF.BlendFlags := 0;
  BF.SourceConstantAlpha := 255;
  BF.AlphaFormat := AC_SRC_ALPHA;

  Winapi.Windows.AlphaBlend(Canvas.Handle, 0, 0, Buff.Width, Buff.Height,
    Buff.Canvas.Handle, 0, 0, Buff.Width, Buff.Height, BF);
end;

Или преобразовать TBitmap32 в Delphi TBitmap и нарисуйте его, используя VCL:

procedure TForm1.FormPaint(Sender: TObject);
var
  Bmp: TBitmap;
  I: Integer;
begin
  Bmp := TBitmap.Create;
  try
    Bmp.PixelFormat := pf32bit;
    Bmp.AlphaFormat := afDefined;
    Bmp.SetSize(Buff.Width, Buff.Height);
    for I := 0 to Buff.Height - 1 do
      Move(Buff.ScanLine[I]^, Bmp.ScanLine[I]^, Buff.Width * 4);
    Canvas.Draw(0, 0, Bmp);
  finally
    Bmp.Free;
  end;
end;
...