From e12aad05f039ddaefd444f80246845090ef9ce13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 28 Nov 2018 16:56:44 +0100 Subject: [PATCH] Add manual test case for verifying our text rendering Allows comparing Qt's text rendering to the native rendering. Change-Id: I56f015fb64df3f70c33db58891876c325cbbc55d Reviewed-by: Allan Sandfeld Jensen Reviewed-by: Eskil Abrahamsen Blomfeldt --- .../manual/textrendering/nativetext/main.cpp | 310 ++++++++++++++++++ .../textrendering/nativetext/nativetext.pro | 7 + tests/manual/textrendering/textrendering.pro | 3 +- 3 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 tests/manual/textrendering/nativetext/main.cpp create mode 100644 tests/manual/textrendering/nativetext/nativetext.pro diff --git a/tests/manual/textrendering/nativetext/main.cpp b/tests/manual/textrendering/nativetext/main.cpp new file mode 100644 index 0000000000..5c7621e61f --- /dev/null +++ b/tests/manual/textrendering/nativetext/main.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifdef Q_OS_DARWIN +#include +#include +#include +#include +#include +#endif + +static int s_mode; +static QString s_text = QString::fromUtf8("The quick brown \xF0\x9F\xA6\x8A jumps over the lazy \xF0\x9F\x90\xB6"); + +class TextRenderer : public QWidget +{ + Q_OBJECT +public: + enum RenderingMode { QtRendering, NativeRendering }; + Q_ENUM(RenderingMode); + + TextRenderer(qreal pointSize, const QString &text, const QColor &textColor = QColor(), const QColor &bgColor = QColor()) + : m_text(text) + { + if (pointSize) { + QFont f = font(); + f.setPointSize(pointSize); + setFont(f); + } + + if (textColor.isValid()) { + QPalette p = palette(); + p.setColor(QPalette::Text, textColor); + setPalette(p); + } + + if (bgColor.isValid()) { + QPalette p = palette(); + p.setColor(QPalette::Window, bgColor); + setPalette(p); + } + } + + QString text() const + { + return !m_text.isNull() ? m_text : s_text; + } + + QSize sizeHint() const override + { + QFontMetrics fm = fontMetrics(); + return QSize(fm.boundingRect(text()).width(), fm.height()); + } + + bool event(QEvent * event) override + { + if (event->type() == QEvent::ToolTip) { + QString toolTip; + QDebug debug(&toolTip); + debug << "textColor =" << palette().color(QPalette::Text) << "bgColor =" << palette().color(QPalette::Window); + setToolTip(toolTip); + } + + return QWidget::event(event); + } + + void paintEvent(QPaintEvent *) override + { + QImage image(size() * devicePixelRatio(), QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(devicePixelRatio()); + + QPainter p(&image); + p.fillRect(QRect(0, 0, image.width(), image.height()), palette().window().color()); + + const int ascent = fontMetrics().ascent(); + + p.setPen(Qt::magenta); + p.drawLine(QPoint(0, ascent), QPoint(width(), ascent)); + p.end(); + + if (s_mode == QtRendering) + renderQtText(image); + else + renderNativeText(image); + + QPainter wp(this); + wp.drawImage(QPoint(0, 0), image); + } + + void renderQtText(QImage &image) + { + QPainter p(&image); + + const int ascent = fontMetrics().ascent(); + + p.setPen(palette().text().color()); + + QFont f = font(); + f.resolve(-1); + p.setFont(f); + + p.drawText(QPoint(0, ascent), text()); + } + + void renderNativeText(QImage &image) + { +#ifdef Q_OS_DARWIN + QMacAutoReleasePool pool; + QMacCGContext ctx(&image); + + const auto *fontEngine = QFontPrivate::get(font())->engineForScript(QChar::Script_Common); + Q_ASSERT(fontEngine); + if (fontEngine->type() == QFontEngine::Multi) { + fontEngine = static_cast(fontEngine)->engine(0); + Q_ASSERT(fontEngine); + } + Q_ASSERT(fontEngine->type() == QFontEngine::Mac); + + QColor textColor = palette().text().color(); + auto nsColor = [NSColor colorWithSRGBRed:textColor.redF() + green:textColor.greenF() + blue:textColor.blueF() + alpha:textColor.alphaF()]; + + if (font().styleStrategy() & QFont::NoAntialias) + CGContextSetShouldAntialias(ctx, false); + + // Retain count already tracked by QMacCGContext above + NSGraphicsContext.currentContext = [NSGraphicsContext graphicsContextWithCGContext:ctx flipped:YES]; + [text().toNSString() drawAtPoint:CGPointZero withAttributes:@{ + NSFontAttributeName : (NSFont *)fontEngine->handle(), + NSForegroundColorAttributeName : nsColor + }]; + NSGraphicsContext.currentContext = nil; +#endif + } + +public: + + RenderingMode m_mode = QtRendering; + QString m_text; +}; + +class TestWidget : public QWidget +{ + Q_OBJECT +public: + TestWidget() + { + auto *mainLayout = new QVBoxLayout; + + m_previews = new QWidget; + m_previews->setLayout(new QHBoxLayout); + + for (int i = 0; i < 6; ++i) { + auto *layout = new QVBoxLayout; + QString text; + if (i > 0) + text = "ABC"; + + QPair color = [i] { + switch (i) { + case 0: return qMakePair(QColor(), QColor()); + case 1: return qMakePair(QColor(Qt::black), QColor(Qt::white)); + case 2: return qMakePair(QColor(Qt::white), QColor(Qt::black)); + case 3: return qMakePair(QColor(Qt::green), QColor(Qt::red)); + case 4: return qMakePair(QColor(0, 0, 0, 128), QColor(Qt::white)); + case 5: return qMakePair(QColor(255, 255, 255, 128), QColor(Qt::black)); + default: return qMakePair(QColor(), QColor()); + } + }(); + + layout->addWidget(new TextRenderer(12, text, color.first, color.second)); + layout->addWidget(new TextRenderer(24, text, color.first, color.second)); + layout->addWidget(new TextRenderer(36, text, color.first, color.second)); + layout->addWidget(new TextRenderer(48, text, color.first, color.second)); + static_cast(m_previews->layout())->addLayout(layout); + } + + mainLayout->addWidget(m_previews); + + auto *controls = new QHBoxLayout; + auto *lineEdit = new QLineEdit(s_text); + connect(lineEdit, &QLineEdit::textChanged, [&](const QString &text) { + s_text = text; + for (TextRenderer *renderer : m_previews->findChildren()) + renderer->updateGeometry(); + }); + controls->addWidget(lineEdit); + + auto *colorButton = new QPushButton("Color..."); + connect(colorButton, &QPushButton::clicked, [&] { + auto *colorDialog = new QColorDialog(this); + colorDialog->setOptions(QColorDialog::NoButtons | QColorDialog::ShowAlphaChannel); + colorDialog->setModal(false); + connect(colorDialog, &QColorDialog::currentColorChanged, [&](const QColor &color) { + QPalette p = palette(); + p.setColor(QPalette::Text, color); + setPalette(p); + }); + colorDialog->setCurrentColor(palette().text().color()); + colorDialog->setVisible(true); + }); + controls->addWidget(colorButton); + auto *fontButton = new QPushButton("Font..."); + connect(fontButton, &QPushButton::clicked, [&] { + auto *fontDialog = new QFontDialog(this); + fontDialog->setOptions(QFontDialog::NoButtons); + fontDialog->setModal(false); + fontDialog->setCurrentFont(m_previews->font()); + connect(fontDialog, &QFontDialog::currentFontChanged, [&](const QFont &font) { + m_previews->setFont(font); + }); + fontDialog->setVisible(true); + }); + controls->addWidget(fontButton); + + auto *aaButton = new QCheckBox("NoAntialias"); + connect(aaButton, &QCheckBox::stateChanged, [&] { + for (TextRenderer *renderer : m_previews->findChildren()) { + QFont font = renderer->font(); + font.setStyleStrategy(QFont::StyleStrategy(font.styleStrategy() ^ QFont::NoAntialias)); + renderer->setFont(font); + } + }); + controls->addWidget(aaButton); + + auto *subpixelAAButton = new QCheckBox("NoSubpixelAntialias"); + connect(subpixelAAButton, &QCheckBox::stateChanged, [&] { + for (TextRenderer *renderer : m_previews->findChildren()) { + QFont font = renderer->font(); + font.setStyleStrategy(QFont::StyleStrategy(font.styleStrategy() ^ QFont::NoSubpixelAntialias)); + renderer->setFont(font); + } + }); + controls->addWidget(subpixelAAButton); + controls->addStretch(); + + mainLayout->addLayout(controls); + + mainLayout->setSizeConstraint(QLayout::SetFixedSize); + setLayout(mainLayout); + + setMode(TextRenderer::QtRendering); + setFocusPolicy(Qt::StrongFocus); + setFocus(); + } + + void setMode(TextRenderer::RenderingMode mode) + { + s_mode = mode; + setWindowTitle(s_mode == TextRenderer::QtRendering ? "Qt" : "Native"); + + for (TextRenderer *renderer : m_previews->findChildren()) + renderer->update(); + } + + void mousePressEvent(QMouseEvent *) override + { + setMode(TextRenderer::RenderingMode(!s_mode)); + } + + void keyPressEvent(QKeyEvent *e) override + { + if (e->key() == Qt::Key_Space) + setMode(TextRenderer::RenderingMode(!s_mode)); + } + + QWidget *m_previews; +}; + +int main(int argc, char **argv) +{ + qputenv("QT_MAX_CACHED_GLYPH_SIZE", "97"); + QApplication app(argc, argv); + + TestWidget widget; + widget.show(); + return app.exec(); +} + +#include "main.moc" + diff --git a/tests/manual/textrendering/nativetext/nativetext.pro b/tests/manual/textrendering/nativetext/nativetext.pro new file mode 100644 index 0000000000..fb3e3799e7 --- /dev/null +++ b/tests/manual/textrendering/nativetext/nativetext.pro @@ -0,0 +1,7 @@ +QT += widgets core-private gui-private +SOURCES += main.cpp +CONFIG -= app_bundle +darwin { + QMAKE_CXXFLAGS += -x objective-c++ + LIBS += -framework Foundation -framework CoreGraphics -framework AppKit +} diff --git a/tests/manual/textrendering/textrendering.pro b/tests/manual/textrendering/textrendering.pro index 92f0741bf3..14806e416c 100644 --- a/tests/manual/textrendering/textrendering.pro +++ b/tests/manual/textrendering/textrendering.pro @@ -1,4 +1,5 @@ TEMPLATE=subdirs SUBDIRS = glyphshaping \ - textperformance + textperformance \ + nativetext