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功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...
随机推荐
- Unity shader学习之屏幕后期处理效果之运动模糊
运动模糊,代码如下: using UnityEngine; public class MotionBlurRenderer : PostEffectRenderer { [Range(0.1f, 0. ...
- Nginx常用功能
3.Nginx常用功能 3.1 反向代理服务器 3.1.1.demo2 a.我在tomcat下部署了一个javaweb项目,tomcat安装的服务器IP为:192.168.37.136,部署的项目在t ...
- Git-什么是分支
为了理解什么是分支,我们先要回顾Git是如何存储数据的. Git并不会保存文件的差异值或者说变化量,而是直接保存文件的快照. 在Git中提交时,会保存一个commit对象,该对象包含一个指向暂存内容快 ...
- Linq To SQL LEFT OUTER JOIN (Left Join)
SQL: SELECT [t0].[ProductName], [t1].[TotalPrice] AS [TotalPrice] FROM [Product] AS [t0] LEFT OUTER ...
- html5-文件的基本格式
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- 如何在Sitecore CMS中管理桌面快捷方式
当您在Sitecore的桌面模式下工作时,创建快捷方式很有用.快捷方式允许您在选择特定项目的情况下打开内容编辑器,而无需深入了解内容树. Sitecore 8 Sitecore 7 Sitecore ...
- flask 对URL进行安全验证
对URL进行安全验证 虽然我们已经实现了重定向会上一个页面的功能,但是安全问题不容忽视,鉴于referer和next容易被串篡改的特性,我们需要对这些值进行验证,否则会形成开放重定向漏洞 以URL ...
- Linux基础命令---IP路由操作
ip ip指令可以显示或操作路由.网路设备,设置路由策略和通道. 此命令的适用范围:RedHat.RHEL.Ubuntu.CentOS.SUSE.openSUSE.Fedora. 1.语法 ...
- web前端学习路线(含20个真实web开发项目集合)
目前web前端工程师日均岗位缺口已经超过50000,随着互联网+的深入发展,html5作为前端展示技术,市场人才需求量将呈直线上涨. Web前端工程师的岗位职责是利用HTML.CSS.Java.DOM ...
- 介绍Python中6个序列的内置类型
1.Python中6个序列的内置类型分别是什么? Python包含6中内建的序列,即列表.元组.字符串.Unicode字符串.buffer对象和 xrange 对象.序列通用的操作包括:索引.长度.组 ...