我在C ++库中使用GraphicsMagick来创建光栅化的输出,该输出主要由文本组成。
我正在做这样的事情:
void gfx_writer::add_text(Magick::Image& img) const
{
using namespace Magick;
const unsigned x = // just a position;
const unsigned y_title = // just a position;
const unsigned y_heading = // just a position;
const unsigned y_value = // just a position;
img.strokeColor("transparent");
img.fillColor("black");
img.font(font_title_);
img.fontPointsize(font_size_title_);
img.draw(DrawableText{static_cast<double>(x), static_cast<double>(y_title), "a text"});
img.font(font_heading_);
img.fontPointsize(font_size_heading_);
img.draw(DrawableText{static_cast<double>(x), static_cast<double>(y_heading), "another text"});
img.font(font_value_);
img.fontPointsize(font_size_value_);
img.draw(DrawableText{static_cast<double>(x), static_cast<double>(y_value), "third text"});
}
font_title_
,font_heading_
和font_value_
是TTF文件的路径。
此操作不止一次,并且我遇到了很差的性能。当我看一下使用Sysinternals Process Monitor会发生什么时,我发现反复读取了TTF文件。所以我的问题是:
img.font(...)
时都会读取TTF文件?答案 0 :(得分:4)
注意:此答案使用ImageMagick的Magick++
库,并且与GraphicsMagick可能有较小的可移植性问题,但基本解决方案相同。
我的观察正确吗,每次调用img.font(...)都会读取TTF文件?
是的,每次都重新加载TTF字体。一种选择是在系统中安装字体,然后调用font-family构造函数。
DrawableFont ( const std::string &family_,
StyleType style_,
const unsigned long weight_,
StretchType stretch_ );
大多数系统都具有某种字体缓存系统,该系统可以加快访问速度,但在现代硬件上并不十分引人注目。
我还想念其他东西吗?
尝试构建图形上下文,并且只调用一次Magick::Image.draw
。请记住,Drawable...
调用仅包装MVG语句,创建std::list<Drawable>
可使您构建复杂的向量。只有在draw
方法使用绘图命令时,TTF才会加载,因此它的键是提前准备所有绘图命令的原因。
让我们从重写您提供的代码开始(我在这里享有一定程度的自由)。
#include <Magick++.h>
const char * font_title_ = "fonts/OpenSans-Regular.ttf";
const char * font_heading_ = "fonts/LiberationMono-Regular.ttf";
const char * font_value_ = "fonts/sansation.ttf";
double font_size_title_ = 32;
double font_size_heading_ = 24;
double font_size_value_ = 16;
void gfx_writer_add_text(Magick::Image& img)
{
using namespace Magick;
double x = 10.0;
double y_title = 10;
double y_heading = 20.0;
double y_value = 30.0;
img.strokeColor("transparent");
img.fillColor("black");
img.font(font_title_);
img.fontPointsize(font_size_title_);
img.draw(DrawableText{x, y_title, "a text"});
img.font(font_heading_);
img.fontPointsize(font_size_heading_);
img.draw(DrawableText{x, y_heading, "another text"});
img.font(font_value_);
img.fontPointsize(font_size_value_);
img.draw(DrawableText{x, y_value, "third text"});
}
int main()
{
Magick::Image img("wizard:");
gfx_writer_add_text(img);
gfx_writer_add_text(img);
gfx_writer_add_text(img);
img.write("output.png");
}
我可以编译和基准化运行时间。我得到以下时间:
$ time ./original.o
real 0m5.061s
user 0m0.094s
sys 0m0.029s
重构代码以使用绘图上下文,并且仅调用一次Magick::Image.draw
。
#include <Magick++.h>
#include <list>
const char * font_title_ = "fonts/OpenSans-Regular.ttf";
const char * font_heading_ = "fonts/LiberationMono-Regular.ttf";
const char * font_value_ = "fonts/sansation.ttf";
double font_size_title_ = 32;
double font_size_heading_ = 24;
double font_size_value_ = 16;
void gfx_writer_add_text(Magick::Image& img)
{
using namespace Magick;
double x = 10.0;
double y_title = 10;
double y_heading = 20.0;
double y_value = 30.0;
std::list<Drawable> ctx;
ctx.push_back(DrawableStrokeColor("transparent"));
ctx.push_back(DrawableFillColor("black"));
/* TITLE */
ctx.push_back(DrawablePushGraphicContext());
ctx.push_back(DrawableFont(font_title_);
ctx.push_back(DrawablePointSize(font_size_title_));
ctx.push_back(DrawableText{x, y_title, "a text"});
ctx.push_back(DrawablePopGraphicContext());
/* HEADING */
ctx.push_back(DrawablePushGraphicContext());
ctx.push_back(DrawableFont(font_heading_));
ctx.push_back(DrawablePointSize(font_size_heading_));
ctx.push_back(DrawableText{x, y_heading, "another text"});
ctx.push_back(DrawablePopGraphicContext());
/* Value */
ctx.push_back(DrawablePushGraphicContext());
ctx.push_back(DrawableFont(font_value_));
ctx.push_back(DrawablePointSize(font_size_value_));
ctx.push_back(DrawableText{x, y_value, "third text"});
ctx.push_back(DrawablePopGraphicContext());
img.draw(ctx);
}
int main()
{
Magick::Image img("wizard:");
gfx_writer_add_text(img);
gfx_writer_add_text(img);
gfx_writer_add_text(img);
img.write("output2.png");
}
基准时间要好一些。
$ time ./with_context.o
real 0m0.106s
user 0m0.090s
sys 0m0.012s
此操作不只一次,而且我的性能也很差。
值得退后一步,问:“如何重构我的解决方案以仅在最后一个可能的时刻绘制?” 。