7

I want to draw text using QPainter, and I want to use QPainterPath first (because ultimately I want to rotate the text in all sorts of ways). However, I find that the text produced by QPainterPath is much uglier than the text produced by QPainter.

The following code:

void MyWidget::paintEvent(QPaintEvent* /*event*/) {

     QFont font;
     font.setStyleHint(QFont::Times, QFont::PreferAntialias);
     font.setPointSize(30);

     QPainter painter;
     painter.begin(this);
     painter.setRenderHint(QPainter::Antialiasing);
     painter.setBrush(Qt::black);
     painter.setFont(font);
     painter.drawText(10, 40, "Hello World");

     QPainterPath textPath;
     textPath.addText(10, 100, font, "Hello world");
     painter.drawPath(textPath);

     painter.end();
}

produces the following result:

enter image description here

The former is clearly much cleaner and nicer, especially in smaller fonts. What should I do to get the same result from QPainterPath?

I'm producing the above results on a Windows 7 computer, with Qt 5.0.

Yellow
  • 3,955
  • 6
  • 45
  • 74

3 Answers3

5

According to the Qt documentation for adding text to a QPainterPath: -

Adds the given text to this path as a set of closed subpaths created from the font supplied.

So there is a conversion going on here, which is why it doesn't look the same. If you need to rotate text, you could try rotating the QPainter before rendering and then restoring it afterwards. Alternatively, if you can use QGraphicsView and QGraphicsDisplay instead of just rendering onto the widget, there is the class QGraphicsTextItem which may help.

But overall, it's the conversion to the set of closed subpaths that is responsible for the different output of text quality.

TheDarkKnight
  • 27,181
  • 6
  • 55
  • 85
  • Right, at least I see what you mean in that there is a conversion going on, although I do not understand what this means in practice (closed subpaths?) and if it's something that can be avoided. Unfortunately, rotating `QPainter` first makes stuff worse, because anti-aliasing is not supported for rotated text. – Yellow Jul 24 '13 at 13:36
  • I know it's Java, but the answer to this question briefly explains subpaths: http://stackoverflow.com/questions/4662295/what-is-path-subpath-in-java2d – TheDarkKnight Jul 24 '13 at 13:58
  • As for asking if it can be avoided, if you're using QPainterPath, I don't think you'll get exactly the same result. You may be able to set some of the painter renderhints which may improve it, such as QPainter::TextAntialiasing. Note that they are flags so can be used in combination. – TheDarkKnight Jul 24 '13 at 14:02
  • 1
    Another possible method would be to add the text with a very large font into the QPainterPath, render this onto a QImage and scale down the image size when you copy to the screen. This may look better, but you'd have to try it. – TheDarkKnight Jul 24 '13 at 14:05
  • I searched several forum threads on the aliasing and rotated text in `QPainter`, and it seems that none of the render flags help. But thanks for the other ideas for hacks, I'm going to give that a try. – Yellow Jul 24 '13 at 14:32
  • No problem. Good luck and let us know how you get on. – TheDarkKnight Jul 24 '13 at 14:53
  • With your advice, I produced what I consider an atrocious but working hack to rotate the text in a sort-of-aliased-not-too-bad way. So you make a picture of the string by drawing on text a `QImage` with a new `QPainter`, and then you rotate the old painter and draw the image on the screen. Not pretty, but it works. :-) – Yellow Jul 25 '13 at 10:10
  • Excellent. Glad to hear you're getting there ;O) – TheDarkKnight Jul 25 '13 at 11:12
5

The two fonts don't look the same because you're adding extra contours to you QPainterPath text. The following piece of code should give good results :

QFont font;
font.setStyleHint(QFont::Times, QFont::PreferAntialias);
font.setPointSize(30);

QPainter painter;
painter.begin(this);
painter.setRenderHints(QPainter::TextAntialiasing | QPainter::Antialiasing);
painter.setFont(font);

// painter text color is modified by setPen()
painter.setPen(Qt::white);
painter.drawText(10, 40, "Hello World 1");

QPainterPath textPath;
textPath.addText(10, 100, font, "Hello World 2");

// painter path text color is modified by setBrush()
painter.setBrush(Qt::white);
// setPen(Qt::white) add extra white contour on text path (what you did)
painter.setPen(Qt::white);
painter.drawPath(textPath);

QPainterPath textPath2;
textPath2.addText(10, 160, font, "Hello World 3");

// painter path text color is modified by setBrush
painter.setBrush(Qt::white);
// setPen(Qt::NoPen) avoid extra contours for QPainter Path
painter.setPen(Qt::NoPen);
painter.drawPath(textPath2);

painter.end();

I admit that QPainterPath text "Hello World 3" is a little bit uglier that QPainterText "Hello World 1", but the result is still better than "Hello World 2"

enter image description here

Mehdi-Antoine
  • 125
  • 1
  • 9
0

I would beg to differ with the above answers and suggest that both the addText/drawPath and the drawText approaches do the same thing and that there likely is not material 'conversion'.

As noted by Mehdi-Antoine - using drawText gives a mid sized text weight, while using addText/drawPath with both a Pen and Brush gives a heavy weight, while using addText/drawPath with only the fill gives a light weight.

Note than the QPen value has a width, defaulting to 1, and if you apply this pen to stroke the outline of the text with a width of 1, the text will appear very heavily as demonstrated by Medhi-Antoine's example.

However - you can achieve effectively identical results to drawText by using the addText/drawPath approach simply by adjusting the weight of the Pen utilized. For a font size of 30 points, Arial, setting a Pen width of 0.2 and then painting using the addText/drawPath approach seems to create something identical to the drawText approach.

It seems that the drawText method takes the colour of the Pen, then uses that to do a fill, then apply a stroke at a specific thickness.

If you're going to use the drawPath method, you'll have to provide bot the Fill and the Pen, and importantly, to adjust the width of the pen appropriately.

jomamaxx
  • 111
  • 5