Blend translucent bitmaps containing text using Graphics32

I am trying to implement a multi-level drawing system inside one of our internal components, and I am having trouble mixing bitmaps containing text.

The following code snippet shows the problem:

uses
  GR32;

procedure DrawBitmaps;
var
  bmp1: TBitmap32;
  bmp2: TBitmap32;
begin
  bmp1 := TBitmap32.Create;
  bmp1.Width := 100;
  bmp1.Height := 100;
  bmp1.FillRect(0, 0, 100, 100, clWhite32);
  bmp1.FillRect(0, 0, 80, 80, clTrGreen32);

  bmp1.Font.Size := -16;
  bmp1.Font.Color := clBlack;
  bmp1.TextOut(2, 10, 'Green');

  bmp1.SaveToFile('c:\0\bmp1.bmp');

  bmp2 := TBitmap32.Create;
  bmp2.Width := 80;
  bmp2.Height := 80;
  bmp2.FillRect(0, 0, 80, 80, clTrRed32);

  bmp2.Font.Size := -16;
  bmp2.Font.Color := clBlack;
  bmp2.TextOut(2, 50, 'Red');

  bmp2.SaveToFile('c:\0\bmp2.bmp');

  bmp2.DrawMode := dmBlend;
  bmp2.DrawTo(bmp1, 20, 20);

  bmp1.SaveToFile('c:\0\bmpcombined.bmp');

  bmp1.Free;
  bmp2.Free;
end;

Resulting Images:

bmp1: bmp1 bmp2: bmp2 bmpcombined:combined

As you can see, the text is black on bmpand bmp2, but looks white on bmpcombined.

I assume that the problem lies in TextOutwhich maps to Windows.ExtTextOut(via GR32_Backends_VCL.pas, TGDIBackend.Textout). This method does not handle transparency and draws text with alpha 00 (color is $ 000000 instead of $ FF000000).

bmp2.Font.Color $FF000000 .

bmp2.Font.Color := TColor(clBlack32);

GitHub

, ?

+4
2

, TextOut , , .

,

procedure TBitmap32.RenderText(X, Y: Integer; const Text: string; AALevel: Integer; Color: TColor32);

.

, , . ( ), - , , ( ).

, VPR (. "TextVPR" ). Graphics32 ( "VPR", , ), . , AGG, FreeType1, .

: , , TextOut, , . , , ( TextOut).

TextVPR , (, ) ( ).

+4

, , .

TransparentToOpaque .

procedure TransparentToOpaque(bmp: TCustomBitmap32);
var
  I: Integer;
  D: PColor32Entry;
begin
  D := PColor32Entry(@bmp.Bits[0]);
  for I := 0 to bmp.Width * bmp.Height - 1 do begin
    if D.A = 0 then
      D.A := $FF;
    Inc(D);
  end;
  bmp.Changed;
end;

FlipTransparency .

procedure FlipTransparency(bmp: TCustomBitmap32);
var
  I: Integer;
  D: PColor32Entry;
begin
  D := PColor32Entry(@bmp.Bits[0]);
  for I := 0 to bmp.Width * bmp.Height - 1 do begin
    if D.A = 0 then
      D.A := $FF
    else if D.A = $FF then
      D.A := 0;
    Inc(D);
  end;
  bmp.Changed;
end;

MakeOpaque .

procedure MakeOpaque(bmp: TCustomBitmap32);
var
  I: Integer;
  D: PColor32Entry;
begin
  D := PColor32Entry(@bmp.Bits[0]);
  for I := 0 to bmp.Width * bmp.Height - 1 do begin
    D.A := $FF;
    Inc(D);
  end;
  bmp.Changed;
end;

.

  • bmp1, , TransparentToOpaque, .

  • () bmp2 bmp3 () . , TextOut.

    • , TextOut bmp3 . FlipTransparency .

    • bmp3 bmp2. () .

    • bmp2 bmp1.

:

procedure TForm53.DrawBitmaps;
var
  bmp1: TBitmap32;
  bmp2: TBitmap32;
  bmp3: TBitmap32;
begin
  bmp1 := TBitmap32.Create;
  bmp1.Width := 100;
  bmp1.Height := 100;
  bmp1.FillRect(0, 0, 100, 100, clWhite32);
  bmp1.FillRect(0, 0, 80, 80, clTrGreen32);

  bmp1.Font.Size := -16;
  bmp1.Font.Color := clBlack;
  bmp1.TextOut(2, 10, 'Green');

  //Mark all fully transparent pixels (generated with TextOut) as opaque.
  TransparentToOpaque(bmp1);

  SaveBitmap32ToPNG(bmp1, 'c:\0\bmp1a.png');

  bmp2 := TBitmap32.Create;
  bmp2.Width := 80;
  bmp2.Height := 80;
  bmp2.FillRect(0, 0, 80, 80, clTrRed32);

  //Create bitmap, large enough to contain drawn text (same size as original bitmap in this example).
  bmp3 := TBitmap32.Create;
  bmp3.Width := bmp2.Width;
  bmp3.Height := bmp2.Height;

  //Copy `bmp2` to `bmp3`.
  bmp2.DrawMode := dmOpaque;
  bmp2.DrawTo(bmp3, 0, 0);

  //Mark all pixels as opaque (alpha = $FF)
  MakeOpaque(bmp3);

  //Draw text on `bmp3`. This will create proper aliasing.
  bmp3.Font.Size := -16;
  bmp3.Font.Color := clBlack;
  bmp3.TextOut(2, 50, 'Red');

  //Make all fully transparent pixels (TextOut) opaque and all fully opaque pixels
  //   (background coming from `bmp2`) transparent.
  FlipTransparency(bmp3);

  SaveBitmap32ToPNG(bmp3, 'c:\0\bmp3a.png');

  //Blend `bmp3` on semi-transparent background (`bmp2`).
  bmp3.DrawMode := dmBlend;
  bmp3.DrawTo(bmp2, 0, 0);

  SaveBitmap32ToPNG(bmp2, 'c:\0\bmp2a.png');

  //Blend background + text onto main image.
  bmp2.DrawMode := dmBlend;
  bmp2.DrawTo(bmp1, 20, 20);

  SaveBitmap32ToPNG(bmp1, 'c:\0\bmpcombineda.png');

  bmp1.Free;
  bmp2.Free;
  bmp3.Free;
end;

:

bmp1a: bmp1a bmp2a: bmp2a bmp3a: bmp3a bmpcombineda: bmpcombineda

+3

Source: https://habr.com/ru/post/1678547/


All Articles