PDA

View Full Version : Drawing antialiased text on Windows very slow



Rawk
10th May 2007, 20:59
I'm using this code:


QPainterPath path;
foreach(QString line, lines)
{
x = xPosition(left, hAlign, width, fontMetrics.width(line));
path.addText(x, y, stdFont, line);
y += lineHeight;
}
QBrush brush(font.color());
QPen pen(font.color(), font.borderF(), Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
if (font.borderF() > 0)
pen.setColor(font.borderColor());
setRenderHint(QPainter::Antialiasing);
setPen(pen);
setBrush(brush);
setFont(stdFont);
drawPath(path);


The code is used in a class which inherits from QPainter. On Linux it runs quite well, but on Windows it takes way too long. If antialiasing isn't enabled the speed is OK, but the text looks pretty bad. Do you have any idea?

wysota
10th May 2007, 21:01
Is there a reason why you use a painter path to draw the text? It should be way faster if you rendered the text directly.

Rawk
11th May 2007, 15:31
Is there a reason why you use a painter path to draw the text? It should be way faster if you rendered the text directly.
If I use drawText() from QPainter, there's no border/outline.

wysota
11th May 2007, 15:49
If you really need it, then I suggest you render the path once to a pixmap and then only render the pixmap to the widget.

Rawk
11th May 2007, 17:20
If you really need it, then I suggest you render the path once to a pixmap and then only render the pixmap to the widget.
I'm doing it this way, but in my opinion if 235 characters need nearly 10 seconds (on a Duron with 1,2 GHz) to draw, then it is far too much.

wysota
11th May 2007, 22:25
They are not characters anymore once you add them to a painter path - they are converted to curves and if the resulting curve is complex (may depend on the font itself) antialiasing it will take very long. On Linux you might be having a lower DPI font or something like that.

Rawk
12th May 2007, 13:13
I can't believe that such a big difference in speed has to do with different fonts.
I've made a little program so you can try it yourself:

main.cpp:


#include <QApplication>
#include "Widget.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
Widget widget;
widget.show();
return app.exec();
}


Widget.h:


#ifndef WIDGET_H
#define WIDGET_H
class QPaintEvent;
#include <QWidget>
class Widget : public QWidget
{
protected:
virtual void paintEvent(QPaintEvent* event);
};
#endif


Widget.cpp


#include "Widget.h"
#include <QtGui>
void Widget::paintEvent(QPaintEvent* /* event */)
{
QStringList lines;
lines << "safsdafsdaf asdflkj a sdfjöl akj " \
<< "asdflkj asdföjl asdf asdkj ösa" \
<< "asödlfjk lkajs dfa asdfjö asöld föl" \
<< "saödlfkj lkj asdf asdfkj klsjd f " \
<< "asdölfkj lökj asdfasdf sakdj" \
<< "asödlfkj ölkj asdf ajsklö fs" \
<< "asdöflkj lökj ölkasjdföalkj ölkj asdf";
QFont font;
font.setPointSize(30);
QPainter p(this);
QFontMetrics fontMetrics(font, p.device());
int lineHeight = fontMetrics.height();
int x = 10;
int y = lineHeight;
QPainterPath path;
foreach(QString line, lines)
{
path.addText(x, y, font, line);
y += lineHeight;
}
QBrush brush(QColor("#FFFFFF"));
QPen pen(QColor("#000000"), 0.5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
p.setRenderHint(QPainter::Antialiasing);
p.setPen(pen);
p.setBrush(brush);
p.setFont(font);
QTime time;
time.start();
p.drawPath(path);
qDebug() << "Time elapsed for drawing:" << time.elapsed() << "ms.";
}


Either there is another, faster way to draw antialiased, outlined text on Windows with Qt or it needs to be improved... If there isn't another way I would even try to use some text rendering outside of Qt. Is there some free (GPL compatible) library or so?

wysota
12th May 2007, 14:29
As I said, once you add text to a painter path, it's not text anymore. This is the problem, not drawing antialiased fonts.

Rawk
12th May 2007, 16:19
OK, the problem is that drawing QPainterPath with antialiasing is slow on Windows... but this still doesn't solve my problem.

wysota
12th May 2007, 16:31
Avoid using painter paths. And if you don't want to, optimise your code. For instance construct the path once and not every time paintEvent is called. The speedup probably won't be very significant, but you have to start with something...

wysota
12th May 2007, 17:10
Try this (You may have to adjust the font name). Is it very unacceptable?

Rawk
14th May 2007, 13:38
Your example works fine, but it draws only one word. Just change the source a little bit to draw a little bit more and you'll see the difference.
I've made the font a little bit smaller and replaced


path.addText(0, 96, fnt, txt);

with


QStringList lines = txt.split("\n");
QFontMetrics fontMetrics(fnt);
int lineHeight = fontMetrics.height();
int y = 0;
foreach(QString line, lines)
{
path.addText(0, y, fnt, line);
y += lineHeight;
}

Use a static color for the brush (otherwise the difference is not so big...). Then let the program draw 6 or more lines of text on Linux and on Windows. On Windows it needs a few seconds. Because my application often has to draw outlined text, I need some faster solution although it is drawn once on a QPixmap. Thanks for trying to help even though I still have no solution.

wysota
14th May 2007, 14:42
I still think you shouldn't use a painter path at all. It's just unreliable for objects where you can't estimate their complexity upfront. And if you do insist on using it - do it once and try to keep the number of nodes in it as small as possible.

Rawk
14th May 2007, 15:13
I don't care whether I use a painter path or not. I just need to draw outlined text in an acceptable time.