(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)的更多相关文章

  1. FireMonkey 源码学习(5)

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

  2. FireMonkey 源码学习(1)

    FireMonkey采用了与VCL不同的机制,很多基础类已经重新编写了,好在一如既往地提供了源代码,故此有机会学习一下. 一.图形引擎 FireMonkey采用了纯图形化技术解决可视化控件,而不是使用 ...

  3. FireMonkey 源码学习(3)

    五.TTextLayoutNG 在FMX.TextLayout.GPU.pas文件中,实现了几个基础功能,其中: (1)渲染单元 在TextLayout中,每一批同字体和颜色的1~n个字符,组成一个最 ...

  4. FireMonkey 源码学习(2)

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

  5. FireMonkey 源码学习(6)

    (6)GetGlyph和GetBaseline TFontGlyphManager是一个抽象类,在不同平台上的实现是不同的,以Windows为例,在FMX.FontGlyphs.Win.pas文件中定 ...

  6. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  7. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

随机推荐

  1. Javascript-do_while....

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  2. beego 初体验 - 环境搭建

    首先,安装go运行时和beego beego,在git bash 运行命令: go get github.com/beego/bee go get github.com/astaxie/beego g ...

  3. laravel使用过程总结

    docker-compose exec -T workspace php artisan route:list  //查看路由 laravel数据存入session,会出现Session store ...

  4. Some Useful Resources for the Future Usage

    并发编程 http://ifeve.com/ 美国各州 http://114.xixik.com/usa-stats/ 美国各州邮编zip code -> https://www.douban. ...

  5. Python全栈-day1-day2-计算机基础

    计算机基础 1.编程语言 语言即事物之间沟通的介质,编程语言即程序员与计算机沟通的介质.程序员通过编写计算机程序使得计算机能够按照人预先的期望执行相应的动作,从而达到在某种程度上解放人和实现人类难以实 ...

  6. Rpgmakermv(7) Chronus插件介绍翻译

    协议:MIT 作用:时间,有时间推进,可以设置速度,随着游戏中时间的推进,会发生昼夜改变和天气变化. ------------------------------------------------- ...

  7. 关于nginx的安装

    今天倒腾nginx,当运用命令service nginx start 出现了这个:Job for nginx.service failed because the control process ex ...

  8. html5-常用的通用元素

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  9. 20155228 2016-2017-2 《Java程序设计》第5周学习总结

    20155228 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 异常处理 try-catch语法:JVM执行try区块中的代码,如果发生错误就会跳到catc ...

  10. SpringMVC中的自定义参数绑定案例

    由于日期数据有很多种格式,所以springmvc没办法把字符串转换成日期类型.所以需要自定义参数绑定.前端控制器接收到请求后,找到注解形式的处理器适配器,对RequestMapping标记的方法进行适 ...