GraphicsMagick TTF字体性能

时间:2019-01-07 09:26:02

标签: c++ performance imagemagick true-type-fonts graphicsmagick

我在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文件?
  • 有没有一种方法可以使用GraphicsMagick OR来缓存字体,以提供除TTF文件路径以外的其他内容?
  • 我想念的其他东西吗?

1 个答案:

答案 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
  

此操作不只一次,而且我的性能也很差。

值得退后一步,问:“如何重构我的解决方案以仅在最后一个可能的时刻绘制?”