5

我正在尝试在我们的一个内部组件中实现分层绘画系统,但在混合包含文本的位图时遇到问题。

以下代码片段显示了问题:

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;

结果图像:

bmp1:bmp1 bmp2:bmp2 bmp组合:结合

如您所见,文本在bmp和上涂成黑色bmp2,但在 上显示为白色bmpcombined

我猜问题在于TextOut映射到Windows.ExtTextOut(通过GR32_Backends_VCL.pasTGDIBackend.Textout)。该方法不处理透明度并使用 alpha 00 绘制文本(颜色为 $000000 而不是 $FF000000)。

作为快速修复,设置bmp2.Font.Color$FF000000没有帮助。

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

我正在使用来自GitHub的新资源

我应该如何在半透明背景上绘制非透明文本,以便将其融合到更大的图片中?

4

2 回答 2

5

据我所知,TextOut 函数只是作为向位图添加一些文本的直接方式,缺少上面提到的所有修复。

为了保持对透明度的完全控制,您可能希望使用

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

反而。

它使用您在自己的答案中提到的技术,但在更复杂的层面上。它还允许您使用抗锯齿(基于过采样),但今天实际上不建议使用除字体引擎输出之外的任何其他抗锯齿技术(以充分利用字体提示)。

当您使用最新的源代码时,您还可以考虑使用 VPR 来呈现文本(参见示例“TextVPR”)。它将文本的轮廓转换为矢量,并使用 Graphics32 的矢量绘图功能(默认使用引擎“VPR”,因此得名)将其渲染到屏幕上。还包括一个精简的 AGG 引擎,它本身基于 FreeType1 引擎,对于字体可能会稍快一些。

说到性能:请记住,除 TextOut 之外的所有其他方法都会明显降低性能。因此,如果您以高性能为目标,可能会更好地编写自己的代码(基于 TextOut)。

否则,TextVPR 方法会为您提供更多自由,尤其是在填充文本(例如使用渐变)或转换文本(转换为曲线等)时。

于 2017-06-06T05:43:23.177 回答
3

我能找到的最佳解决方案需要三个辅助函数。

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

于 2017-06-05T13:47:01.513 回答