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;