我有一个大小不断变化的图像,我想知道我应该使用哪种字体大小来适应动态变化的大小。
如您所知,有一种Graphics.MeasureString方法可以计算字符串的大小。一种可能的方法是测量每个字体大小,直到找到最合适的字体大小,但由于我需要在一秒钟内渲染很多帧,因此性能影响太大了。
给定特定的图像宽度,是否有更有效的方法来查找字体大小?
首先,我很确定没有一种方法不使用Graphics.MeasureString(). 至少,您必须分析 GDI+ 字体渲染代码的重要部分才能估计最终字体大小,因为它使用自己的渲染器。
幸运的是,有一些方法可以将MeasureString()每帧的调用次数大大减少到一个小常数(甚至为零),具体取决于您使用的字体。
如果您使用等宽字体,事情就很简单:
n。调用次数MeasureString():n启动时的次数,0每帧的次数。
如果您使用字符大小可变的比例字体,事情会变得更加复杂。正如前言所说,打电话是难免的MeasureString();但是,通过计算和细化估计,您可以显着减少对小常数 的调用所需次数。
通用算法是:
max) 字体大小。max。s可以使用比例因子来计算估计的字体大小:est = s * max。est尚未达到最佳状态,请稍微调整字体大小。一个实现可能如下所示:
public Font GetFont(string str, Graphics g, int imgWidth, int imgHeight)
{
// Measure with maximum sized font
var baseSize = g.MeasureString(str, _fontCache[_maxFontSize]);
// Downsample to actual image size
float widthRatio = imgWidth / baseSize.Width;
float heightRatio = imgHeight / baseSize.Height;
float minRatio = Math.Min(widthRatio, heightRatio);
int estimatedFontSize = (int)(_maxFontSize * minRatio);
// Make sure the precomputed font list is always hit
if(estimatedFontSize > _maxFontSize)
estimatedFontSize = _maxFontSize;
else if(estimatedFontSize < _minFontSize)
estimatedFontSize = _minFontSize;
// Make sure the estimated size is not too large
var estimatedSize = g.MeasureString(str, _fontCache[estimatedFontSize]);
bool estimatedSizeWasReduced = false;
while(estimatedSize.Width > imgWidth || estimatedSize.Height > imgHeight)
{
if(estimatedFontSize == _minFontSize)
break;
--estimatedFontSize;
estimatedSizeWasReduced = true;
estimatedSize = g.MeasureString(str, _fontCache[estimatedFontSize]);
++counter;
}
// Can we increase the size a bit?
if(!estimatedSizeWasReduced)
{
while(estimatedSize.Width < imgWidth && estimatedSize.Height < imgHeight)
{
if(estimatedFontSize == _maxFontSize)
break;
++estimatedFontSize;
estimatedSize = g.MeasureString(str, _fontCache[estimatedFontSize]);
}
// We increase the size until it is larger than the image, so we need to go back one step afterwards
if(estimatedFontSize > _minFontSize)
--estimatedFontSize;
}
return _fontCache[estimatedFontSize];
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,各种 C# 在线编译器不支持 GDI+,但我上传了一个独立的示例程序作为 Gist。该程序测试了各种不同的图像大小,同时记录了对MeasureString().
调用次数MeasureString():0启动期间的次数,2或3每帧的次数。
因此,(在实践中)该算法具有恒定的复杂性,并且比线性方法更有效,同时它仍然可以找到最佳的整数字体大小。
MeasureString()如果特定字体和字符集允许,可以通过添加额外的检查来进一步优化此方法,以便节省另外一两个调用。
注意事项:
MeasureString()较小。n Font对象并将它们放入列表中_fontCache。从性能角度来看,这可能没有必要;您可能想要测量分配Font对象的开销。max。升级也是可能的,但您可能会失去精度。m可能的字符串,则缓存其计算出的字体大小可能会有所帮助。