From 1348b33591182e16264ff15854efe849638ca04c Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 12 Jul 2010 14:42:37 +0200 Subject: 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 --- src/gui/painting/qpainter.cpp | 68 ++++++++++++++++---- src/gui/text/qtextlayout.cpp | 36 ++++++++--- 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(fontEngine)->engine(engineIdx); } + QFixed leftMost; + QFixed rightMost; + QFixed baseLine; + for (int i=0; iboundingBox(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 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 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 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 glyphsHash; + QHash, QGlyphs> glyphsHash; QList keys = glyphLayoutHash.uniqueKeys(); for (int i=0; i QTextLine::glyphs() const for (int j=0; j glyphsArray; QVarLengthArray positionsArray; @@ -2283,10 +2299,12 @@ QList QTextLine::glyphs() const glyphIndexes.setGlyphIndexes(glyphs); glyphIndexes.setPositions(positions); - if (!glyphsHash.contains(fontEngine)) - glyphsHash.insert(fontEngine, QGlyphs()); + QPair 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() << 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" -- cgit v0.12 From 81856e8b008099e39465543c3e85049380256cbb Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 13 Jul 2010 16:47:26 +0200 Subject: Support RTL text in QGlyphs RTL text would be positioned wrong with QGlyphs, as we would not correctly detect the direction of the text and pass it into getGlyphPositions(). The bidi analysis is the same as in the QTextLine::draw() method. Reviewed-by: Kim --- src/gui/text/qtextlayout.cpp | 5 +++- tests/auto/qglyphs/tst_qglyphs.cpp | 49 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index 4dda114..43900c0 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -2228,6 +2228,8 @@ QList QTextLine::glyphs() const flags |= QTextItem::Underline; if (font.strikeOut()) flags |= QTextItem::StrikeOut; + if (si.analysis.bidiLevel % 2) + flags |= QTextItem::RightToLeft; QGlyphLayout glyphLayout = eng->shapedGlyphs(&si).mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart); @@ -2285,7 +2287,8 @@ QList QTextLine::glyphs() const QVarLengthArray glyphsArray; QVarLengthArray positionsArray; - fontEngine->getGlyphPositions(glyphLayout, QTransform(), 0, glyphsArray, positionsArray); + fontEngine->getGlyphPositions(glyphLayout, QTransform(), flags, glyphsArray, + positionsArray); Q_ASSERT(glyphsArray.size() == positionsArray.size()); QVector glyphs; diff --git a/tests/auto/qglyphs/tst_qglyphs.cpp b/tests/auto/qglyphs/tst_qglyphs.cpp index 6827b23..b75e801 100644 --- a/tests/auto/qglyphs/tst_qglyphs.cpp +++ b/tests/auto/qglyphs/tst_qglyphs.cpp @@ -69,6 +69,7 @@ private slots: void drawStruckOutText(); void drawOverlinedText(); void drawUnderlinedText(); + void drawRightToLeft(); void detach(); private: @@ -527,6 +528,54 @@ void tst_QGlyphs::drawUnderlinedText() QCOMPARE(textLayoutDraw, drawGlyphs); } +void tst_QGlyphs::drawRightToLeft() +{ +#if defined(Q_WS_MAC) + QSKIP("Unstable because of QTBUG-11145", SkipAll); +#endif + + QString s; + s.append(QChar(1575)); + s.append(QChar(1578)); + + QPixmap textLayoutDraw(1000, 1000); + QPixmap drawGlyphs(1000, 1000); + + textLayoutDraw.fill(Qt::white); + drawGlyphs.fill(Qt::white); + + 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("drawRightToLeft_textLayoutDraw.png"); + drawGlyphs.save("drawRightToLeft_drawGlyphIndexes.png"); +#endif + + QCOMPARE(textLayoutDraw, drawGlyphs); + +} + QTEST_MAIN(tst_QGlyphs) #include "tst_qglyphs.moc" -- cgit v0.12 From 37f585ece17063a814598221c0cbb874c52d223f Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 15 Jul 2010 10:05:11 +0200 Subject: Support glyphs that are wider than default width in texture glyph cache We need to reserve space for the widest possible glyph in the font Reviewed-by: Kim --- src/gui/painting/qtextureglyphcache.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gui/painting/qtextureglyphcache.cpp b/src/gui/painting/qtextureglyphcache.cpp index 29cd82b..376219b 100644 --- a/src/gui/painting/qtextureglyphcache.cpp +++ b/src/gui/painting/qtextureglyphcache.cpp @@ -127,8 +127,12 @@ void QTextureGlyphCache::populate(QFontEngine *fontEngine, int numGlyphs, const rowHeight += margin * 2 + paddingDoubled; - if (m_w == 0) - m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; + if (m_w == 0) { + if (fontEngine->maxCharWidth() <= QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH) + m_w = QT_DEFAULT_TEXTURE_GLYPH_CACHE_WIDTH; + else + m_w = qt_next_power_of_two(fontEngine->maxCharWidth()); + } // now actually use the coords and paint the wanted glyps into cache. QHash::iterator iter = listItemCoordinates.begin(); -- cgit v0.12 From aada50574b844d30f5e8da0111590bbd92070d74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Nilsen?= Date: Thu, 15 Jul 2010 13:55:11 +0200 Subject: Compile with qmlscene. Reviewed-by: kim --- src/opengl/qgl_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index dfbc926..cdf6539 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -416,7 +416,7 @@ public: #endif #if defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QWS) - static QGLExtensionFuncs qt_extensionFuncs; + static Q_OPENGL_EXPORT QGLExtensionFuncs qt_extensionFuncs; static inline QGLExtensionFuncs& extensionFuncs(const QGLContext *) { return qt_extensionFuncs; } #endif -- cgit v0.12