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功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
随机推荐
- mint-ui Picker的使用
<template> <div v-bind:style="{minHeight:clientHeight + 'px'}" id="recive-mi ...
- python pillow
https://www.cnblogs.com/morethink/p/8419151.html#%E7%9B%B4%E6%8E%A5%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA ...
- leetcode Sort List 对链表进行排序
描述: Sort a linked list in O(n log n) time using constant space complexity. 在O(n*log(n))的时间复杂度,常数级空间复 ...
- web api HttpConfiguration
//设置web api configuration public static void Register(HttpConfiguration config){ config.Services.Rep ...
- Linux基础命令---文本过滤coi
col 过滤掉影响阅读的控制字符,使用重定向符把说明手册的内容输出到文本文件时,控制字符就成乱码.col指令可以过滤掉控制字符,使文本可读.col从标砖输入读取内容,输出到标准输出.col在读取字符时 ...
- selenium-java web自动化测试工具抓取百度搜索结果实例
selenium-java web自动化测试工具抓取百度搜索结果实例 这种方式抓百度的搜索关键字结果非常容易抓长尾关键词,根据热门关键词去抓更多内容可以用抓google,百度的这种内容容易给屏蔽,用这 ...
- bzoj3196 二逼平衡树
题目链接 平衡树系列最后一题 坑啊 10s时间限制跑了9764ms...还是要学一学bit套主席树啦... 经典的线段树套treap...至于第一发为什么要TLE(我不会告诉你treap插入的时候忘了 ...
- [转载]web安全之token
参考:http://blog.csdn.net/sum_rain/article/details/37085771 Token,就是令牌,最大的特点就是随机性,不可预测.一般黑客或软件无法猜测出来. ...
- dubbo原理
1,观察DubboBeanDefinitionParser 的构造方法,给它打一个断点,发现其前一步在DubboNamespaceHandler 应用启动会连续调此方法 DubboBeanDefini ...
- Q_DECL_OVERRIDE
Q_DECL_OVERRIDE也就是c++的override # define Q_DECL_OVERRIDE override 在重写虚函数时会用到, 作用是防止写错虚函数: void keyPre ...