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 := to FFrame.Count - do
begin
LLine := FFrame[I];
for J := to LLine.Count - 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 := / 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 := to FFrame.Count - do
begin
LLine := FFrame[I];
{
定位每一行的左上顶点位置
}
Pos := LLine.TopLeft + TopLeft; {
每行有若干渲染单元,对每个渲染单元进行处理
}
for J := to LLine.Count - 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 := to LRun.Chars.Count - 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 /
else
Thickness := Self.Font.Size / ;
FStrokeBrush.Thickness := Thickness;
{
删除线
Pos的位置为最后一个字符的后面
}
if TFontStyle.fsStrikeOut in Styles then
ACanvas.DrawLine(
TPointF.Create(Pos.X - LRun.ImageRect.Width, Pos.Y + LRun.ImageRect.Height / ),
TPointF.Create(Pos.X, Pos.Y + LRun.ImageRect.Height / ),
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函数。
FireMonkey 源码学习(4)的更多相关文章
- FireMonkey 源码学习(5)
(5)UpdateCharRec 该函数的源码分析如下: procedure TTextLayoutNG.UpdateCharRec(const ACanvas: TCanvas; NeedBitma ...
- FireMonkey 源码学习(1)
FireMonkey采用了与VCL不同的机制,很多基础类已经重新编写了,好在一如既往地提供了源代码,故此有机会学习一下. 一.图形引擎 FireMonkey采用了纯图形化技术解决可视化控件,而不是使用 ...
- FireMonkey 源码学习(3)
五.TTextLayoutNG 在FMX.TextLayout.GPU.pas文件中,实现了几个基础功能,其中: (1)渲染单元 在TextLayout中,每一批同字体和颜色的1~n个字符,组成一个最 ...
- FireMonkey 源码学习(2)
三.TControl FireMonkey重写了TControl的代码,实现了众多接口,如下图: 基类上实现了众多功能,这里不详细描述. 四.TEdit 编辑框是从TControl—TStyledCo ...
- FireMonkey 源码学习(6)
(6)GetGlyph和GetBaseline TFontGlyphManager是一个抽象类,在不同平台上的实现是不同的,以Windows为例,在FMX.FontGlyphs.Win.pas文件中定 ...
- Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结
2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...
- jQuery源码学习感想
还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...
- MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)
前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...
- MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)
前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
随机推荐
- SV通过DPI调用C
Verilog与C之间进行程序交互,PLI(Programming Language Interface)经过了TF,ACC,VPI等模式. 使用PLI可以生成延时计算器,来连接和同步多个仿真器,并可 ...
- HTML&CSS书写规范
第一部分:HTML书写规范: 1.1 HTML整体结构: 1.1.1:HTML基础设施: 文档以"<!DOCTYPE...>"首行顶格开始,推荐使用"< ...
- Marlin 溫度 sensor 校正
Marlin 溫度 sensor 校正 使用 Type-K 溫度計 將探針綑綁在加熱頭側面 開啟Marlin-Marlin_v1\Marlin\thermistortables.h 要修改的溫度對應表 ...
- PGPDesktop在win7环境下的安装和使用
PGPDesktop在win7环境下的安装和使用 PGP的简介 PGP(Pretty Good Privacy),是一个基于RSA公钥加密体系的邮件加密软件,它提供了非对称加密和数字签名,是目前非常流 ...
- python windows安装 SQLServer pymssql,
1.到正儿八经的网站下载文件,找到适合自己的版本 2.把文件放到一个地方,能让pip找到就行, 不放scripts下面的话, 恐怕会报错“FileNotFoundError" 3. 走到pi ...
- 【转】阿里出品的ETL工具dataX初体验
原文链接:https://www.imooc.com/article/15640 来源:慕课网 我的毕设选择了大数据方向的题目.大数据的第一步就是要拿到足够的数据源.现实情况中我们需要的数据源分布在不 ...
- selenium-grid 分布式 实现同一脚本在不同pc上运行
1.使用版本:selenium 3.11.0chrome 65phantomjs 2.1.1selenium-server selenium-server-standalone-2.46.0.jar ...
- Linux基础命令---查找进程id
pidof pidof可以查找指定名称的进程的pid,将结果送到标准输出.pidof有两种返回值:0,找到至少一个进程:1,没有找到进程.pidof实际上与killall5相同:程序根据调用它的名称进 ...
- Linux基础命令---文本显示tac
tac 将指定文件中的行,按照反序方式显示.此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. 1.语法 tac [选项] ...
- linux mysql主从复制
centos7 安装 mariadb 1 yum 源 -- 配置阿里的 2 rmp 方式 3 源码编译方式 -- 专业DBA 一些知识点: 虚拟环境 不影响 redis/ mariadb/mysq ...