(4)DoDrawLayout

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

procedure TTextLayoutNG.DoDrawLayout(const ACanvas: TCanvas);
varCharDic: 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;
beginif Text.IsEmpty thenExit;{在渲染函数 DoRenderLayout 中记录了原来的颜色,如果当前颜色与渲染时颜色不一致,则重新设置}if FOldColor <> Color thenbeginFOldColor := Color;for I := 0 to FFrame.Count - 1 dobeginLLine := FFrame[I];for J := 0 to LLine.Count - 1 dobeginLRun := LLine[J];LRun.SetColor(Color, True);end;end;end;{检查画布 Canvas 的缩放比例与当前比例是否一致,如果不一致则重新渲染}if not SameValue(FScale, ACanvas.Scale, Epsilon) thenbeginFScale := 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 dobeginLLine := FFrame[I];{定位每一行的左上顶点位置}Pos := LLine.TopLeft + TopLeft;{每行有若干渲染单元,对每个渲染单元进行处理}for J := 0 to LLine.Count - 1 dobeginLRun := LLine[J];{得到渲染单元的字体类型TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut);TFontStyles = set of TFontStyle;}if LRun.Font <> nil thenStyles := LRun.Font.StyleelseStyles := Self.Font.Style;{每个渲染单元是按照 字体 + 颜色 进行区分的,即若干相同字体和颜色的字符,纳入到一个渲染单元中对每个渲染单元,按照其对应的字体获取 字符渲染处理对象(TCharDic)每个字符渲染处理对象记录了在当前字体下,该字符对应的 字形、大小,并预先绘制好图片。其核心思想应该是:预先将每个字符在不同的字体下,绘制到一个图片中保存,当需要绘制时,将这些图片直接绘制到画布上,以加快渲染速度PCharRec = ^TCharRec;TCharRec = recordGlyph: 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 dobegin{得到该渲染单元对应的预先绘制好的字符图片记录,如果没有预先绘制,则立即处理...UpdateCharRec(...)}Rec := AddOrGetChar(ACanvas, LRun.Chars[K], CharDic, LRun.Font);{如果图片存在,则处理其绘制的位置}if Assigned(Rec.Bitmap) thenbegin{计算水平位置}if HorizontalAligned thenR.Left := ACanvas.AlignToPixelHorizontally(Pos.X) + Rec.Glyph.Origin.X * FScaleFactorelseR.Left := Pos.X + Rec.Glyph.Origin.X * FScaleFactor;{计算垂直位置}if VerticalAligned thenR.Top := ACanvas.AlignToPixelVertically(Pos.Y) + Rec.Glyph.Origin.Y * FScaleFactorelseR.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 thenbeginClipBounds := 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;endelse{不裁剪则进行平滑处理}beginR.Inflate(FSmoothRenderMarginInPixels, FSmoothRenderMarginInPixels);SrcR.Inflate(SmoothRenderMargin, SmoothRenderMargin);end;{判断是否彩色,并变更画布的调制色彩?}ColoredGlyph := TFontGlyphStyle.ColorGlyph in Rec.Glyph.Style;if ColoredGlyph thenTCustomCanvasGpu(ACanvas).ModulateColor := $FFFFFFFF;{将预先绘制好的内容绘制到画布上}ACanvas.DrawBitmap(Rec.Bitmap, SrcR, R, Opacity);{还原画布的调制色彩}if ColoredGlyph thenTCustomCanvasGpu(ACanvas).ModulateColor := LRun.Color;end;{移动横坐标到本字符的后面,以便进行下一个字符绘制}Pos.X := Pos.X + (Rec.Glyph.Advance * FScaleFactor);end;{}if LRun.IsTrimmed thenbeginRec := AddOrGetChar(ACanvas, FEllipsisChar, GetCharDictionary(Self.Font), Self.Font);TCustomCanvasGpu(ACanvas).ModulateColor := Self.Color;if Assigned(Rec.Bitmap) thenbeginif HorizontalAligned thenR.Left := ACanvas.AlignToPixelHorizontally(Pos.X) + Rec.Glyph.Origin.X * FScaleFactorelseR.Left := Pos.X + Rec.Glyph.Origin.X * FScaleFactor;if VerticalAligned thenR.Top := ACanvas.AlignToPixelVertically(Pos.Y) + Rec.Glyph.Origin.Y * FScaleFactorelseR.Top := Pos.Y + Rec.Glyph.Origin.Y * FScaleFactor;R.Right := R.Left + (Rec.SrcRect.Width * FScaleFactor);R.Bottom := R.Top + (Rec.SrcRect.Height * FScaleFactor);// DrawR.Inflate(FSmoothRenderMarginInPixels, FSmoothRenderMarginInPixels);SrcR.Inflate(SmoothRenderMargin, SmoothRenderMargin);ACanvas.DrawBitmap(Rec.Bitmap, Rec.SrcRect, R, Opacity);end;end;{处理下划线和删除线}if ([TFontStyle.fsStrikeOut, TFontStyle.fsUnderline] * Styles) <> [] thenbegin{颜色}FStrokeBrush.Color := LRun.Color;{线条厚度}if LRun.Font <> nil thenThickness := LRun.Font.Size / 15elseThickness := Self.Font.Size / 15;FStrokeBrush.Thickness := Thickness;{删除线Pos的位置为最后一个字符的后面}if TFontStyle.fsStrikeOut in Styles thenACanvas.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 thenACanvas.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函数。

FireMonkey 源码学习(4)相关推荐

  1. FireMonkey 源码学习(5)

    (5)UpdateCharRec 该函数的源码分析如下: procedure TTextLayoutNG.UpdateCharRec(const ACanvas: TCanvas; NeedBitma ...

  2. FireMonkey 源码学习(2)

    三.TControl FireMonkey重写了TControl的代码,实现了众多接口,如下图: 基类上实现了众多功能,这里不详细描述. 四.TEdit 编辑框是从TControl-TStyledCo ...

  3. Shiro源码学习之二

    接上一篇 Shiro源码学习之一 3.subject.login 进入login public void login(AuthenticationToken token) throws Authent ...

  4. Shiro源码学习之一

    一.最基本的使用 1.Maven依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId&g ...

  5. mutations vuex 调用_Vuex源码学习(六)action和mutation如何被调用的(前置准备篇)...

    前言 Vuex源码系列不知不觉已经到了第六篇.前置的五篇分别如下: 长篇连载:Vuex源码学习(一)功能梳理 长篇连载:Vuex源码学习(二)脉络梳理 作为一个Web前端,你知道Vuex的instal ...

  6. vue实例没有挂载到html上,vue 源码学习 - 实例挂载

    前言 在学习vue源码之前需要先了解源码目录设计(了解各个模块的功能)丶Flow语法. src ├── compiler # 把模板解析成 ast 语法树,ast 语法树优化,代码生成等功能. ├── ...

  7. 2021-03-19Tomcat源码学习--WebAppClassLoader类加载机制

    Tomcat源码学习--WebAppClassLoader类加载机制 在WebappClassLoaderBase中重写了ClassLoader的loadClass方法,在这个实现方法中我们可以一窥t ...

  8. jQuery源码学习之Callbacks

    jQuery源码学习之Callbacks jQuery的ajax.deferred通过回调实现异步,其实现核心是Callbacks. 使用方法 使用首先要先新建一个实例对象.创建时可以传入参数flag ...

  9. JDK源码学习笔记——Integer

    一.类定义 public final class Integer extends Number implements Comparable<Integer> 二.属性 private fi ...

最新文章

  1. python字符串、元组常用操作
  2. 腾讯天津数据中心余热回收应用初探
  3. 《Python游戏编程快速上手》第十一章猜数字,推理游戏Bagels
  4. 在HTML中使用javascript (js高级程序设计)
  5. SurfaceView类透明背景设置
  6. virtual.lab motion用表达式控制载荷
  7. [ZJOI 2006]书架
  8. PCL学习(4.5)——点云对象的两种定义方式的区别与转换
  9. 《Android 第1行代码》读后感—第1章【开始启程,你的第一行Android代码】
  10. 自己闲着没事整理的人工智能思维导图(2(鸢尾花))
  11. eslint 换行_给 eslint 写一个插件
  12. Skyscrapers Aren’t Scalable
  13. 大于号--小于号转义符
  14. 远程服务器下载百度网盘中的内容
  15. Unity ShaderLab Stencil Comp 枚举的对应数值
  16. 除adsense外适合英文站的国外广告联盟(4/12/2011更新)
  17. root格式化linux,Linux-格式化与检验-mkfs
  18. kubernetes的DevOps业务(七):Jenkins,GitLab,Harbor,Tekton,GitOps
  19. 一文读懂NLP之隐马尔科夫模型(HMM)详解加python实现
  20. 计算机切换管理员用户,切换为Administrator,完全掌握电脑

热门文章

  1. A Juggling Algorithm (旋转交换)
  2. Pipeline Alpha版本项目展示
  3. ajax成功跨域_自己写的
  4. 关系型数据库和非关系型数据库的区别
  5. Netty 的 内存池 是如何实现的
  6. iisS7 配置SSL 绑定主机头实现多站点访问
  7. kafka专题:kafka单机和集群安装详情,Spring Boot如何整合Kafka
  8. 垃圾回收算法与实现系列-锁在Java虚拟机中的实现和优化
  9. 使用Docker安装Redis
  10. mac服务器 文件无法删除文件夹,macOS 10.15.4 无法删除一个空文件夹