FireMonkey 源码学习(6)

(6)GetGlyph和GetBaseline

TFontGlyphManager是一个抽象类,在不同平台上的实现是不同的,以Windows为例,在FMX.FontGlyphs.Win.pas文件中定义了:

TWinFontGlyphManager = class(TFontGlyphManager)
   ...
  protected
    function DoGetGlyph(const Char: UCS4Char; const Settings: TFontGlyphSettings): TFontGlyph; override;
    function DoGetBaseline: Single; override;
  ...

DoGetGlyph的实现代码如下:

function TWinFontGlyphManager.DoGetGlyph(const Char: UCS4Char; const Settings: TFontGlyphSettings): TFontGlyph;
var
  CharsString: string;
  Abc: TABCFLOAT;
  CharSize: TSize;
  GlyphRect: TRect;
  I, J: Integer;
  C: Byte;
  Color: TAlphaColorRec;
  GlyphStyle: TFontGlyphStyles;
  Bitmap: TWinBitmap;
begin
  {
    获取字符在画布上的宽度和高度
  }
  CharsString := System.Char.ConvertFromUtf32(Char);
  GetTextExtentPoint32W(FMeasureBitmap.DC, CharsString, 1, CharSize);
  GetCharABCWidthsFloat(FMeasureBitmap.DC, Char, Char, Abc);

  {
    采用Bitmap模式
  }
  if TFontGlyphSetting.Bitmap in Settings then
  begin
    {
      构建Bitmap
    }
    Bitmap := CreateBitmap(Max(Ceil(Abs(Abc.abcfA) + Abs(Abc.abcfB) + Abs(Abc.abcfC)), CharSize.cx), CharSize.cy);
    FillLongword(Bitmap.Bits, Bitmap.Width * Bitmap.Height, TAlphaColorRec.White);

    {
      设置颜色
    }
    SetTextColor(Bitmap.DC, RGB(0, 0, 0));
    SetBkColor(Bitmap.DC, RGB(255, 255, 255));

    {
       设置文字对齐方式
    }
    SetTextAlign(Bitmap.DC, TA_TOP);
    {
       将字符绘制在Bitmap的画布上
    }
    SelectObject(Bitmap.DC, FFont);
    TextOut(Bitmap.DC, -Trunc(Abc.abcfA), 0, @Char, 1);

    {
      找到最小的,不透明的位置
    }
    GlyphRect := TRect.Create(Bitmap.Width, CharSize.cy, 0, 0);
    for I := 0 to Bitmap.Width - 1 do
      for J := 0 to CharSize.cy - 1 do
      begin
        C := Bitmap.Bits[J * Bitmap.Width + I].R;
        if C > 0 then
        begin
          if J < GlyphRect.Top then
            GlyphRect.Top := J;
          if I < GlyphRect.Left then
            GlyphRect.Left := I;
        end;
      end;
    for I := Bitmap.Width - 1 downto GlyphRect.Left do
      for J := CharSize.cy - 1 downto GlyphRect.Top do
      begin
        C := Bitmap.Bits[J * Bitmap.Width + I].R;
        if C > 0 then
        begin
          if J > GlyphRect.Bottom then
            GlyphRect.Bottom := J;
          if I > GlyphRect.Right then
            GlyphRect.Right := I;
        end;
      end;
    GlyphRect.Left := Min(CharSize.cx, GlyphRect.Left);
    GlyphRect.Top := Min(CharSize.cy, GlyphRect.Top);
    GlyphRect.Right := Max(CharSize.cx, GlyphRect.Right + 1);
    GlyphRect.Bottom := Max(CharSize.cy, GlyphRect.Bottom + 1);
  end;

  {
    判断是否存在Glyph
  }
  GlyphStyle := [];
  if not HasGlyph(Char) then
    GlyphStyle := [TFontGlyphStyle.NoGlyph];

  {
    构建该字体下、该字符的Glyph
  }
  Result := TFontGlyph.Create(TPoint.Create(GlyphRect.Left + Trunc(Abc.abcfA), GlyphRect.Top),
    Abc.abcfA + Abc.abcfB + Abc.abcfC, CharSize.Height, GlyphStyle);

  {
    Bitmap模式下,将Glyph复制到Bitmap
  }
  if TFontGlyphSetting.Bitmap in Settings then
  begin
    Result.Bitmap.SetSize(Max(GlyphRect.Width, GlyphRect.Right), GlyphRect.Height, TPixelFormat.BGRA);

    for I := GlyphRect.Left to GlyphRect.Right - 1 do
      for J := GlyphRect.Top to GlyphRect.Bottom - 1 do
      begin
        Color := Bitmap.Bits[J * Bitmap.Width + I];
        if Color.R < 255 then
        begin
          { Faster integer variant of formula:
            Result = R * 0.2126 + G * 0.7152 + B * 0.0722 }
          C := 255 - ((Integer(Color.R * 54) + Integer(Color.G * 183) + Integer(Color.B * 19)) div 256);
          if TFontGlyphSetting.PremultipliedAlpha in Settings then
            Result.Bitmap.Pixels[I - GlyphRect.Left, J - GlyphRect.Top] := MakeColor(C, C, C, C)
          else
            Result.Bitmap.Pixels[I - GlyphRect.Left, J - GlyphRect.Top] := MakeColor($FF, $FF, $FF, C);
        end;
      end;
    DestroyBitmap(Bitmap);
  end;
end;

DoGetBaseline的实现代码直接返回了FBaseline的值,FBaseline是在载入资源时生成,其核心函数为LoadResource,代码分析如下:

procedure TWinFontGlyphManager.LoadResource;
var
  Height: Integer;
  dwBold, dwItalic: Cardinal;
  Metrics: TTextMetric;
begin
  {
    字体高度、Style属性等
  }
  Height := -Round(CurrentSettings.Size * CurrentSettings.Scale);
  if TFontStyle.fsBold in CurrentSettings.Style then
    dwBold := FW_BOLD
  else
    dwBold := FW_NORMAL;
  if TFontStyle.fsItalic in CurrentSettings.Style then
    dwItalic := 1
  else
    dwItalic := 0;
  {
    Windows的API创建Font
  }
  FFont := CreateFont(Height, 0, 0, 0, dwBold, dwItalic,
    0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
    CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
    DEFAULT_PITCH or FF_DONTCARE, PChar(CurrentSettings.Family));
  if FFont = 0 then
    Exit;
  SelectObject(FMeasureBitmap.DC, FFont);
  {
    获取画布的字体各种信息,其中Metrics的定义如下:    
  tagTEXTMETRICW = record
    tmHeight: Longint;
    tmAscent: Longint;   //Specifies the ascent (units above the base line) of characters. 
    tmDescent: Longint;
    tmInternalLeading: Longint;
    tmExternalLeading: Longint;
    tmAveCharWidth: Longint;
    tmMaxCharWidth: Longint;
    tmWeight: Longint;
    tmOverhang: Longint;
    tmDigitizedAspectX: Longint;
    tmDigitizedAspectY: Longint;
    tmFirstChar: WideChar;
    tmLastChar: WideChar;
    tmDefaultChar: WideChar;
    tmBreakChar: WideChar;
    tmItalic: Byte;
    tmUnderlined: Byte;
    tmStruckOut: Byte;
    tmPitchAndFamily: Byte;
    tmCharSet: Byte;
  end;
  }
  GetTextMetrics(FMeasureBitmap.DC, Metrics);
  FBaseline := Metrics.tmAscent;
end;
posted @ 2014-06-17 22:32  玻璃城市  阅读(1489)  评论(0编辑  收藏  举报