zoukankan      html  css  js  c++  java
  • 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函数。

  • 相关阅读:
    使用CSS将图片转换成黑白(灰色、置灰)
    require.js
    Tortoisegit安装下载
    谷歌浏览器添加扩展程序
    IOS 照相问题
    java的interface和PHP的区别
    Object中的hashCode方法
    PHP、js、Java中的匿名函数、闭包、回调函数
    Java和PHP中的浅克隆和深克隆
    unicode字符编码中一些专业词汇的全称
  • 原文地址:https://www.cnblogs.com/ChinaEHR/p/3793515.html
Copyright © 2011-2022 走看看