首页 > 代码库 > FireMonkey 源码学习(4)

FireMonkey 源码学习(4)

(4)DoDrawLayout

DoDrawLayout函数的源代码分析如下:

procedure TTextLayoutNG.DoDrawLayout(const ACanvas: TCanvas);var  CharDic: TCharDic;  Rec: PCharRec;  Pos: TPointF;  R, SrcR, ClipBounds: TRectF;  LLine: TGPULine;  LRun: TGPURun;  I, J, K: Integer;  VerticalAligned, HorizontalAligned, ColoredGlyph: Boolean;  Styles: TFontStyles;  Thickness: Single;begin  if Text.IsEmpty then    Exit;  {    在渲染函数 DoRenderLayout 中记录了原来的颜色,    如果当前颜色与渲染时颜色不一致,则重新设置  }  if FOldColor <> Color then  begin    FOldColor := Color;    for I := 0 to FFrame.Count - 1 do    begin      LLine := FFrame[I];      for J := 0 to LLine.Count - 1 do      begin        LRun := LLine[J];        LRun.SetColor(Color, True);      end;    end;  end;  {     检查画布 Canvas 的缩放比例与当前比例是否一致,     如果不一致则重新渲染  }  if not SameValue(FScale, ACanvas.Scale, Epsilon) then  begin    FScale := ACanvas.Scale;    FScaleFactor := 1 / FScale;    DoRenderLayout;  end;  {    检查左上顶点是否为0,以此判断是否按照 横向和纵向 进行排列布局  }  HorizontalAligned := SameValue(Frac(TopLeft.X), 0.0, TEpsilon.Position) and SameValue(Frac(ACanvas.Matrix.m31), 0.0, TEpsilon.Position);  VerticalAligned := SameValue(Frac(TopLeft.Y), 0.0, TEpsilon.Position) and SameValue(Frac(ACanvas.Matrix.m32), 0.0, TEpsilon.Position);  {    对输出的帧进行处理,帧(Frame)、行(Line)、渲染单元(Run) 的关系如下  --------GPUFrame-------------  |(GPURun)(GPURun)...(GPURun)| <- GPULine (several GPURun‘s with different font and/or color)  |(GPURun)                   | <- GPULine (no additional styling, so only a single GPURun)  |(GPURun)                   | <- GPULine  |                           | ...  |                           |  |                           |  -----------------------------    帧有若干行,对每一行进行处理  }  for I := 0 to FFrame.Count - 1 do  begin    LLine := FFrame[I];    {      定位每一行的左上顶点位置    }    Pos := LLine.TopLeft + TopLeft;    {      每行有若干渲染单元,对每个渲染单元进行处理    }    for J := 0 to LLine.Count - 1 do    begin      LRun := LLine[J];      {        得到渲染单元的字体类型        TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut);        TFontStyles = set of TFontStyle;      }      if LRun.Font <> nil then        Styles := LRun.Font.Style      else        Styles := Self.Font.Style;      {        每个渲染单元是按照 字体 + 颜色 进行区分的,        即若干相同字体和颜色的字符,纳入到一个渲染单元中        对每个渲染单元,按照其对应的字体获取 字符渲染处理对象(TCharDic)        每个字符渲染处理对象记录了在当前字体下,该字符对应的 字形、大小,并预先绘制好图片。        其核心思想应该是:          预先将每个字符在不同的字体下,绘制到一个图片中保存,          当需要绘制时,将这些图片直接绘制到画布上,以加快渲染速度        PCharRec = ^TCharRec;        TCharRec = record          Glyph: TFontGlyph;          SrcRect: TRectF;          Bitmap: TBitmap;        end;        TCharDic = class(TDictionary<UCS4Char, PCharRec>)        还有一种做法是,将绘制过程放入TPathData中存储,在绘制时直接调用Path进行快速绘制      }      CharDic := GetCharDictionary(LRun.Font);      {        画布的调制色彩?      }      TCustomCanvasGpu(ACanvas).ModulateColor := LRun.Color;      {        对渲染单元的每一个字符进行处理      }      for K := 0 to LRun.Chars.Count - 1 do      begin        {          得到该渲染单元对应的预先绘制好的字符图片记录,          如果没有预先绘制,则立即处理          ...          UpdateCharRec(...)        }        Rec := AddOrGetChar(ACanvas, LRun.Chars[K], CharDic, LRun.Font);        {           如果图片存在,则处理其绘制的位置        }        if Assigned(Rec.Bitmap) then        begin          {            计算水平位置          }          if HorizontalAligned then            R.Left := ACanvas.AlignToPixelHorizontally(Pos.X) + Rec.Glyph.Origin.X * FScaleFactor          else            R.Left := Pos.X + Rec.Glyph.Origin.X * FScaleFactor;          {            计算垂直位置          }          if VerticalAligned then            R.Top := ACanvas.AlignToPixelVertically(Pos.Y) + Rec.Glyph.Origin.Y * FScaleFactor          else            R.Top := Pos.Y + Rec.Glyph.Origin.Y * FScaleFactor;          {            计算宽度            Rec.SrcRect代表原始宽度,再乘以比例          }          R.Right := R.Left + (Rec.SrcRect.Width * FScaleFactor);          R.Bottom := R.Top + (Rec.SrcRect.Height * FScaleFactor);          SrcR := Rec.SrcRect;          {             根据是否裁剪进行处理 ,             如果裁剪则设置裁剪的范围          }          if LRun.IsClipped then          begin            ClipBounds := LRun.ClipBounds[K];            SrcR.Top := SrcR.Top + ClipBounds.Top * FScale;            R.Top := R.Top + ClipBounds.Top;            SrcR.Bottom := SrcR.Bottom - ClipBounds.Bottom * FScale;            R.Bottom := R.Bottom - ClipBounds.Bottom;            SrcR.Left := SrcR.Left + ClipBounds.Left * FScale;            R.Left := R.Left + ClipBounds.Left;            SrcR.Right := SrcR.Right - ClipBounds.Right * FScale;            R.Right := R.Right - ClipBounds.Right;          end          else          {             不裁剪则进行平滑处理          }          begin            R.Inflate(FSmoothRenderMarginInPixels, FSmoothRenderMarginInPixels);            SrcR.Inflate(SmoothRenderMargin, SmoothRenderMargin);          end;          {             判断是否彩色,并变更画布的调制色彩?          }          ColoredGlyph := TFontGlyphStyle.ColorGlyph in Rec.Glyph.Style;          if ColoredGlyph then            TCustomCanvasGpu(ACanvas).ModulateColor := $FFFFFFFF;          {             将预先绘制好的内容绘制到画布上          }          ACanvas.DrawBitmap(Rec.Bitmap, SrcR, R, Opacity);          {             还原画布的调制色彩          }          if ColoredGlyph then            TCustomCanvasGpu(ACanvas).ModulateColor := LRun.Color;        end;        {           移动横坐标到本字符的后面,以便进行下一个字符绘制        }        Pos.X := Pos.X + (Rec.Glyph.Advance * FScaleFactor);      end;      {              }      if LRun.IsTrimmed then      begin        Rec := AddOrGetChar(ACanvas, FEllipsisChar, GetCharDictionary(Self.Font), Self.Font);        TCustomCanvasGpu(ACanvas).ModulateColor := Self.Color;        if Assigned(Rec.Bitmap) then        begin          if HorizontalAligned then            R.Left := ACanvas.AlignToPixelHorizontally(Pos.X) + Rec.Glyph.Origin.X * FScaleFactor          else            R.Left := Pos.X + Rec.Glyph.Origin.X * FScaleFactor;          if VerticalAligned then            R.Top := ACanvas.AlignToPixelVertically(Pos.Y) + Rec.Glyph.Origin.Y * FScaleFactor          else            R.Top := Pos.Y + Rec.Glyph.Origin.Y * FScaleFactor;          R.Right := R.Left + (Rec.SrcRect.Width * FScaleFactor);          R.Bottom := R.Top + (Rec.SrcRect.Height * FScaleFactor);          // Draw          R.Inflate(FSmoothRenderMarginInPixels, FSmoothRenderMarginInPixels);          SrcR.Inflate(SmoothRenderMargin, SmoothRenderMargin);          ACanvas.DrawBitmap(Rec.Bitmap, Rec.SrcRect, R, Opacity);        end;      end;      {        处理下划线和删除线      }      if ([TFontStyle.fsStrikeOut, TFontStyle.fsUnderline] * Styles) <> [] then      begin        {           颜色        }        FStrokeBrush.Color := LRun.Color;        {           线条厚度        }        if LRun.Font <> nil then          Thickness := LRun.Font.Size / 15        else          Thickness := Self.Font.Size / 15;        FStrokeBrush.Thickness := Thickness;        {           删除线           Pos的位置为最后一个字符的后面        }        if TFontStyle.fsStrikeOut in Styles then          ACanvas.DrawLine(            TPointF.Create(Pos.X - LRun.ImageRect.Width, Pos.Y + LRun.ImageRect.Height / 2),            TPointF.Create(Pos.X, Pos.Y + LRun.ImageRect.Height / 2),            Opacity,      //不透明            FStrokeBrush);        {           下划线        }        if TFontStyle.fsUnderline in Styles then          ACanvas.DrawLine(            TPointF.Create(Pos.X - LRun.ImageRect.Width, Pos.Y + CharDic.Baseline * FScaleFactor + 1.5 * Thickness),            TPointF.Create(Pos.X, Pos.Y + CharDic.Baseline * FScaleFactor + 1.5 * Thickness),            Opacity,            FStrokeBrush);      end;      //下一个渲染单元    end;    //下一行  end;  TCustomCanvasGpu(ACanvas).ModulateColor := $FFFFFFFF;end;

一个重要的函数数:AddOrGetChar,获取字符处理对象,调用了UpdateCharRec函数。