diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com> | 2010-07-12 12:42:37 (GMT) |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com> | 2010-07-14 11:39:28 (GMT) |
commit | 1348b33591182e16264ff15854efe849638ca04c (patch) | |
tree | 9073fb87f69a2472fb1a296e1fd78f73e09500b9 | |
parent | 5bcb42d8f47f7206e9c0776b67a4c990ac93aa34 (diff) | |
download | Qt-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.cpp | 68 | ||||
-rw-r--r-- | src/gui/text/qtextlayout.cpp | 36 | ||||
-rw-r--r-- | tests/auto/qglyphs/tst_qglyphs.cpp | 126 |
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" |