summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2010-07-12 12:42:37 (GMT)
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com>2010-07-14 11:39:28 (GMT)
commit1348b33591182e16264ff15854efe849638ca04c (patch)
tree9073fb87f69a2472fb1a296e1fd78f73e09500b9
parent5bcb42d8f47f7206e9c0776b67a4c990ac93aa34 (diff)
downloadQt-1348b33591182e16264ff15854efe849638ca04c.zip
Qt-1348b33591182e16264ff15854efe849638ca04c.tar.gz
Qt-1348b33591182e16264ff15854efe849638ca04c.tar.bz2
Support text decoration in QPainter::drawGlyphs()
In order to support text decorations (overline, underline and strikeout) in QPainter::drawGlyphs(), we need to call drawTextItemDecoration() from this function. To support this, it has been generalized to no longer require a QTextItemInt. We also need to propagate the decoration attributes of the font from QTextLayout to QGlyphs, since this is not part of the QFontEngine. Task-number: QTBUG-12122 Reviewed-by: Kim
-rw-r--r--src/gui/painting/qpainter.cpp68
-rw-r--r--src/gui/text/qtextlayout.cpp36
-rw-r--r--tests/auto/qglyphs/tst_qglyphs.cpp126
3 files changed, 208 insertions, 22 deletions
diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp
index a845842..d6fbab3 100644
--- a/src/gui/painting/qpainter.cpp
+++ b/src/gui/painting/qpainter.cpp
@@ -88,6 +88,10 @@ bool qt_show_painter_debug_output = true;
extern QPixmap qt_pixmapForBrush(int style, bool invert);
+static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, QFontEngine *fe,
+ QTextCharFormat::UnderlineStyle underlineStyle,
+ QTextItemInt::RenderFlags flags, qreal width,
+ const QTextCharFormat &charFormat);
void qt_format_text(const QFont &font,
const QRectF &_r, int tf, const QTextOption *option, const QString& str, QRectF *brect,
int tabstops, int* tabarray, int tabarraylen,
@@ -5750,6 +5754,8 @@ void qt_draw_glyphs(QPainter *painter, const quint32 *glyphArray, const QPointF
void QPainterPrivate::drawGlyphs(quint32 *glyphArray, QFixedPoint *positions, int glyphCount)
{
+ Q_Q(QPainter);
+
updateState(state);
QFontEngine *fontEngine = state->font.d->engineForScript(QUnicodeTables::Common);
@@ -5764,6 +5770,26 @@ void QPainterPrivate::drawGlyphs(quint32 *glyphArray, QFixedPoint *positions, in
fontEngine = static_cast<QFontEngineMulti *>(fontEngine)->engine(engineIdx);
}
+ QFixed leftMost;
+ QFixed rightMost;
+ QFixed baseLine;
+ for (int i=0; i<glyphCount; ++i) {
+ glyph_metrics_t gm = fontEngine->boundingBox(glyphArray[i]);
+ if (i == 0 || leftMost > positions[i].x)
+ leftMost = positions[i].x;
+
+ // We don't support glyphs that do not share a common baseline. If this turns out to
+ // be a relevant use case, then we need to find clusters of glyphs that share a baseline
+ // and do a drawTextItemDecorations call per cluster.
+ if (i == 0 || baseLine < positions[i].y)
+ baseLine = positions[i].y;
+
+ // We use the advance rather than the actual bounds to match the algorithm in drawText()
+ if (i == 0 || rightMost < positions[i].x + gm.xoff)
+ rightMost = positions[i].x + gm.xoff;
+ }
+
+ QFixed width = rightMost - leftMost;
if (extended != 0) {
QStaticTextItem staticTextItem;
@@ -5797,6 +5823,21 @@ void QPainterPrivate::drawGlyphs(quint32 *glyphArray, QFixedPoint *positions, in
engine->drawTextItem(QPointF(0, 0), textItem);
}
+
+ QTextItemInt::RenderFlags flags;
+ if (state->font.underline())
+ flags |= QTextItemInt::Underline;
+ if (state->font.overline())
+ flags |= QTextItemInt::Overline;
+ if (state->font.strikeOut())
+ flags |= QTextItemInt::StrikeOut;
+
+ drawTextItemDecoration(q, QPointF(leftMost.toReal(), baseLine.toReal()),
+ fontEngine,
+ (state->font.underline()
+ ? QTextCharFormat::SingleUnderline
+ : QTextCharFormat::NoUnderline),
+ flags, width.toReal(), QTextCharFormat());
}
/*!
@@ -6322,15 +6363,15 @@ static QPixmap generateWavyPixmap(qreal maxRadius, const QPen &pen)
return pixmap;
}
-static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const QTextItemInt &ti)
+static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, QFontEngine *fe,
+ QTextCharFormat::UnderlineStyle underlineStyle,
+ QTextItemInt::RenderFlags flags, qreal width,
+ const QTextCharFormat &charFormat)
{
- QTextCharFormat::UnderlineStyle underlineStyle = ti.underlineStyle;
if (underlineStyle == QTextCharFormat::NoUnderline
- && !(ti.flags & (QTextItem::StrikeOut | QTextItem::Overline)))
+ && !(flags & (QTextItem::StrikeOut | QTextItem::Overline)))
return;
- QFontEngine *fe = ti.fontEngine;
-
const QPen oldPen = painter->pen();
const QBrush oldBrush = painter->brush();
painter->setBrush(Qt::NoBrush);
@@ -6339,7 +6380,7 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const
pen.setWidthF(fe->lineThickness().toReal());
pen.setCapStyle(Qt::FlatCap);
- QLineF line(pos.x(), pos.y(), pos.x() + ti.width.toReal(), pos.y());
+ QLineF line(pos.x(), pos.y(), pos.x() + width, pos.y());
const qreal underlineOffset = fe->underlinePosition().toReal();
// deliberately ceil the offset to avoid the underline coming too close to
@@ -6354,21 +6395,21 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const
painter->save();
painter->translate(0, pos.y() + 1);
- QColor uc = ti.charFormat.underlineColor();
+ QColor uc = charFormat.underlineColor();
if (uc.isValid())
pen.setColor(uc);
// Adapt wave to underlineOffset or pen width, whatever is larger, to make it work on all platforms
const QPixmap wave = generateWavyPixmap(qMax(underlineOffset, pen.widthF()), pen);
- const int descent = (int) ti.descent.toReal();
+ const int descent = (int) fe->descent().toReal();
painter->setBrushOrigin(painter->brushOrigin().x(), 0);
- painter->fillRect(pos.x(), 0, qCeil(ti.width.toReal()), qMin(wave.height(), descent), wave);
+ painter->fillRect(pos.x(), 0, qCeil(width), qMin(wave.height(), descent), wave);
painter->restore();
} else if (underlineStyle != QTextCharFormat::NoUnderline) {
QLineF underLine(line.x1(), underlinePos, line.x2(), underlinePos);
- QColor uc = ti.charFormat.underlineColor();
+ QColor uc = charFormat.underlineColor();
if (uc.isValid())
pen.setColor(uc);
@@ -6380,14 +6421,14 @@ static void drawTextItemDecoration(QPainter *painter, const QPointF &pos, const
pen.setStyle(Qt::SolidLine);
pen.setColor(oldPen.color());
- if (ti.flags & QTextItem::StrikeOut) {
+ if (flags & QTextItem::StrikeOut) {
QLineF strikeOutLine = line;
strikeOutLine.translate(0., - fe->ascent().toReal() / 3.);
painter->setPen(pen);
painter->drawLine(strikeOutLine);
}
- if (ti.flags & QTextItem::Overline) {
+ if (flags & QTextItem::Overline) {
QLineF overLine = line;
overLine.translate(0., - fe->ascent().toReal());
painter->setPen(pen);
@@ -6528,7 +6569,8 @@ void QPainter::drawTextItem(const QPointF &p, const QTextItem &_ti)
else
d->engine->drawTextItem(p, ti);
}
- drawTextItemDecoration(this, p, ti);
+ drawTextItemDecoration(this, p, ti.fontEngine, ti.underlineStyle, ti.flags, ti.width.toReal(),
+ ti.charFormat);
if (d->state->renderHints != oldRenderHints) {
d->state->renderHints = oldRenderHints;
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index 3bad6a5..4dda114 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -2183,13 +2183,15 @@ static void setPenAndDrawBackground(QPainter *p, const QPen &defaultPen, const Q
namespace {
struct GlyphInfo
{
- GlyphInfo(const QGlyphLayout &layout, const QPointF &position)
- : glyphLayout(layout), itemPosition(position)
+ GlyphInfo(const QGlyphLayout &layout, const QPointF &position,
+ const QTextItemInt::RenderFlags &renderFlags)
+ : glyphLayout(layout), itemPosition(position), flags(renderFlags)
{
}
QGlyphLayout glyphLayout;
QPointF itemPosition;
+ QTextItem::RenderFlags flags;
};
}
@@ -2218,6 +2220,15 @@ QList<QGlyphs> QTextLine::glyphs() const
QPointF pos(iterator.x.toReal(), y);
QFont font = eng->font(si);
+
+ QTextItem::RenderFlags flags;
+ if (font.overline())
+ flags |= QTextItem::Overline;
+ if (font.underline())
+ flags |= QTextItem::Underline;
+ if (font.strikeOut())
+ flags |= QTextItem::StrikeOut;
+
QGlyphLayout glyphLayout = eng->shapedGlyphs(&si).mid(iterator.glyphsStart,
iterator.glyphsEnd - iterator.glyphsStart);
@@ -2235,7 +2246,7 @@ QList<QGlyphs> QTextLine::glyphs() const
QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
glyphLayoutHash.insertMulti(multiFontEngine->engine(which),
- GlyphInfo(subLayout, pos));
+ GlyphInfo(subLayout, pos, flags));
start = end;
which = e;
@@ -2243,16 +2254,16 @@ QList<QGlyphs> QTextLine::glyphs() const
QGlyphLayout subLayout = glyphLayout.mid(start, end - start);
glyphLayoutHash.insertMulti(multiFontEngine->engine(which),
- GlyphInfo(subLayout, pos));
+ GlyphInfo(subLayout, pos, flags));
} else {
glyphLayoutHash.insertMulti(mainFontEngine,
- GlyphInfo(glyphLayout, pos));
+ GlyphInfo(glyphLayout, pos, flags));
}
}
}
- QHash<QFontEngine *, QGlyphs> glyphsHash;
+ QHash<QPair<QFontEngine *, int>, QGlyphs> glyphsHash;
QList<QFontEngine *> keys = glyphLayoutHash.uniqueKeys();
for (int i=0; i<keys.size(); ++i) {
@@ -2265,6 +2276,11 @@ QList<QGlyphs> QTextLine::glyphs() const
for (int j=0; j<glyphLayouts.size(); ++j) {
const QPointF &pos = glyphLayouts.at(j).itemPosition;
const QGlyphLayout &glyphLayout = glyphLayouts.at(j).glyphLayout;
+ const QTextItem::RenderFlags &flags = glyphLayouts.at(j).flags;
+
+ font.setOverline(flags.testFlag(QTextItem::Overline));
+ font.setUnderline(flags.testFlag(QTextItem::Underline));
+ font.setStrikeOut(flags.testFlag(QTextItem::StrikeOut));
QVarLengthArray<glyph_t> glyphsArray;
QVarLengthArray<QFixedPoint> positionsArray;
@@ -2283,10 +2299,12 @@ QList<QGlyphs> QTextLine::glyphs() const
glyphIndexes.setGlyphIndexes(glyphs);
glyphIndexes.setPositions(positions);
- if (!glyphsHash.contains(fontEngine))
- glyphsHash.insert(fontEngine, QGlyphs());
+ QPair<QFontEngine *, int> key(fontEngine, int(flags));
+
+ if (!glyphsHash.contains(key))
+ glyphsHash.insert(key, QGlyphs());
- QGlyphs &target = glyphsHash[fontEngine];
+ QGlyphs &target = glyphsHash[key];
target += glyphIndexes;
target.setFont(font);
}
diff --git a/tests/auto/qglyphs/tst_qglyphs.cpp b/tests/auto/qglyphs/tst_qglyphs.cpp
index 54a69dc..6827b23 100644
--- a/tests/auto/qglyphs/tst_qglyphs.cpp
+++ b/tests/auto/qglyphs/tst_qglyphs.cpp
@@ -66,6 +66,9 @@ private slots:
void drawNonExistentGlyphs();
void drawMultiScriptText1();
void drawMultiScriptText2();
+ void drawStruckOutText();
+ void drawOverlinedText();
+ void drawUnderlinedText();
void detach();
private:
@@ -401,6 +404,129 @@ void tst_QGlyphs::detach()
QCOMPARE(glyphs.glyphIndexes(), QVector<quint32>() << 1 << 2 << 3);
}
+void tst_QGlyphs::drawStruckOutText()
+{
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setStrikeOut(true);
+
+ QTextLayout layout(s);
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphs glyphs = layout.glyphs().size() > 0
+ ? layout.glyphs().at(0)
+ : QGlyphs();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphs(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawStruckOutText_textLayoutDraw.png");
+ drawGlyphs.save("drawStruckOutText_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
+void tst_QGlyphs::drawOverlinedText()
+{
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setOverline(true);
+
+ QTextLayout layout(s);
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphs glyphs = layout.glyphs().size() > 0
+ ? layout.glyphs().at(0)
+ : QGlyphs();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphs(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawOverlineText_textLayoutDraw.png");
+ drawGlyphs.save("drawOverlineText_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
+void tst_QGlyphs::drawUnderlinedText()
+{
+ QPixmap textLayoutDraw(1000, 1000);
+ QPixmap drawGlyphs(1000, 1000);
+
+ textLayoutDraw.fill(Qt::white);
+ drawGlyphs.fill(Qt::white);
+
+ QString s = QString::fromLatin1("Foobar");
+
+ QFont font;
+ font.setUnderline(true);
+
+ QTextLayout layout(s);
+ layout.setFont(font);
+ layout.beginLayout();
+ layout.createLine();
+ layout.endLayout();
+
+ {
+ QPainter p(&textLayoutDraw);
+ layout.draw(&p, QPointF(50, 50));
+ }
+
+ QGlyphs glyphs = layout.glyphs().size() > 0
+ ? layout.glyphs().at(0)
+ : QGlyphs();
+
+ {
+ QPainter p(&drawGlyphs);
+ p.drawGlyphs(QPointF(50, 50), glyphs);
+ }
+
+#if defined(DEBUG_SAVE_IMAGE)
+ textLayoutDraw.save("drawUnderlineText_textLayoutDraw.png");
+ drawGlyphs.save("drawUnderlineText_drawGlyphIndexes.png");
+#endif
+
+ QCOMPARE(textLayoutDraw, drawGlyphs);
+}
+
QTEST_MAIN(tst_QGlyphs)
#include "tst_qglyphs.moc"