diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp index 2cf6aa92dd9..791e17f1d69 100644 --- a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp @@ -163,27 +163,39 @@ static DWRITE_MEASURING_MODE renderModeToMeasureMode(DWRITE_RENDERING_MODE rende } } +static QFont::HintingPreference determineHinting(const QFontDef &fontDef) +{ + QFont::HintingPreference hintingPreference = QFont::HintingPreference(fontDef.hintingPreference); + if (hintingPreference == QFont::PreferDefaultHinting) { + if (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0)) { + // Microsoft documentation recommends using asymmetric rendering for small fonts + // at pixel size 16 and less, and symmetric for larger fonts. + hintingPreference = fontDef.pixelSize > 16.0 + ? QFont::PreferNoHinting + : QFont::PreferVerticalHinting; + } else { + hintingPreference = QFont::PreferFullHinting; + } + } + + return hintingPreference; +} + DWRITE_RENDERING_MODE QWindowsFontEngineDirectWrite::hintingPreferenceToRenderingMode(const QFontDef &fontDef) const { if ((fontDef.styleStrategy & QFont::NoAntialias) && glyphFormat != QFontEngine::Format_ARGB) return DWRITE_RENDERING_MODE_ALIASED; - QFont::HintingPreference hintingPreference = QFont::HintingPreference(fontDef.hintingPreference); - if (!qFuzzyCompare(qApp->devicePixelRatio(), 1.0) && hintingPreference == QFont::PreferDefaultHinting) { - // Microsoft documentation recommends using asymmetric rendering for small fonts - // at pixel size 16 and less, and symmetric for larger fonts. - hintingPreference = fontDef.pixelSize > 16.0 - ? QFont::PreferNoHinting - : QFont::PreferVerticalHinting; - } - + QFont::HintingPreference hintingPreference = determineHinting(fontDef); switch (hintingPreference) { case QFont::PreferNoHinting: return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; case QFont::PreferVerticalHinting: return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; default: - return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC; + return fontDef.pixelSize > 16.0 + ? DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC + : DWRITE_RENDERING_MODE_GDI_CLASSIC; } } @@ -558,15 +570,17 @@ void QWindowsFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QFontEn QVarLengthArray glyphMetrics(glyphIndices.size()); HRESULT hr; - DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef); + QFont::HintingPreference hint = determineHinting(fontDef); bool needsDesignMetrics = shaperFlags & QFontEngine::DesignMetrics; - if (!needsDesignMetrics && (renderMode == DWRITE_RENDERING_MODE_GDI_CLASSIC - || renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL - || renderMode == DWRITE_RENDERING_MODE_ALIASED)) { + if (!needsDesignMetrics && hint == QFont::PreferFullHinting) { + const DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef); + const bool needsNaturalMetrics = renderMode == DWRITE_RENDERING_MODE_NATURAL + || renderMode == DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC; + hr = m_directWriteFontFace->GetGdiCompatibleGlyphMetrics(float(fontDef.pixelSize), 1.0f, NULL, - renderMode == DWRITE_RENDERING_MODE_GDI_NATURAL, + needsNaturalMetrics, glyphIndices.data(), glyphIndices.size(), glyphMetrics.data()); @@ -763,11 +777,10 @@ QImage QWindowsFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, bool QWindowsFontEngineDirectWrite::supportsHorizontalSubPixelPositions() const { - DWRITE_RENDERING_MODE renderMode = hintingPreferenceToRenderingMode(fontDef); + QFont::HintingPreference hinting = determineHinting(fontDef); return (!isColorFont() - && renderMode != DWRITE_RENDERING_MODE_GDI_CLASSIC - && renderMode != DWRITE_RENDERING_MODE_GDI_NATURAL - && renderMode != DWRITE_RENDERING_MODE_ALIASED); + && hinting != QFont::PreferFullHinting + && !(fontDef.styleStrategy & QFont::NoAntialias)); } QFontEngine::Properties QWindowsFontEngineDirectWrite::properties() const diff --git a/tests/manual/textrendering/fontenginecomparison/CMakeLists.txt b/tests/manual/textrendering/fontenginecomparison/CMakeLists.txt new file mode 100644 index 00000000000..fc36718ad48 --- /dev/null +++ b/tests/manual/textrendering/fontenginecomparison/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(vrs LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(fontenginecomparison + GUI + SOURCES + main.cpp + mainwindow.cpp mainwindow.h mainwindow.ui + LIBRARIES + Qt::Gui + Qt::Widgets + ENABLE_AUTOGEN_TOOLS + uic + +) diff --git a/tests/manual/textrendering/fontenginecomparison/main.cpp b/tests/manual/textrendering/fontenginecomparison/main.cpp new file mode 100644 index 00000000000..c8fc4d84b38 --- /dev/null +++ b/tests/manual/textrendering/fontenginecomparison/main.cpp @@ -0,0 +1,51 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" + +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + if (a.arguments().size() > 5) { + QString fontFamily = a.arguments().at(1); + int fontSize = a.arguments().at(2).toInt(); + QString example = a.arguments().at(3); + int weight = a.arguments().at(4).toInt(); + bool isItalic = a.arguments().at(5).toInt(); + + QFont font(fontFamily); + font.setPixelSize(fontSize); + font.setWeight(QFont::Weight(weight)); + font.setItalic(isItalic); + + QTextLayout layout; + layout.setFont(font); + layout.setText(example); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QRect brect = layout.boundingRect().toAlignedRect(); + + QImage image(brect.size(), QImage::Format_RGB32); + image.fill(Qt::white); + image.setDevicePixelRatio(1.0); + + QPainter p; + p.begin(&image); + layout.draw(&p, -brect.topLeft()); + p.end(); + + image.save(QStringLiteral("output.png")); + + return 0; + } else { + MainWindow w; + w.show(); + return a.exec(); + } +} diff --git a/tests/manual/textrendering/fontenginecomparison/mainwindow.cpp b/tests/manual/textrendering/fontenginecomparison/mainwindow.cpp new file mode 100644 index 00000000000..24123316851 --- /dev/null +++ b/tests/manual/textrendering/fontenginecomparison/mainwindow.cpp @@ -0,0 +1,86 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include "mainwindow.h" +#include "ui_mainwindow.h" + +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent) + , ui(new Ui::MainWindow) +{ + ui->setupUi(this); + + + ui->cbWeight->addItem(QStringLiteral("Thin"), QFont::Thin); + ui->cbWeight->addItem(QStringLiteral("ExtraLight"), QFont::ExtraLight); + ui->cbWeight->addItem(QStringLiteral("Light"), QFont::Light); + ui->cbWeight->addItem(QStringLiteral("Normal"), QFont::Normal); + ui->cbWeight->addItem(QStringLiteral("Medium"), QFont::Medium); + ui->cbWeight->addItem(QStringLiteral("DemiBold"), QFont::DemiBold); + ui->cbWeight->addItem(QStringLiteral("Bold"), QFont::Bold); + ui->cbWeight->addItem(QStringLiteral("ExtraBold"), QFont::ExtraBold); + ui->cbWeight->addItem(QStringLiteral("Black"), QFont::Black); + ui->cbWeight->setCurrentIndex(3); + + updateFont(); + + connect(ui->sbPixelSize, &QSpinBox::valueChanged, this, &MainWindow::updateFont); + connect(ui->fontComboBox, &QFontComboBox::currentFontChanged, this, &MainWindow::updateFont); + connect(ui->rbDefault, &QRadioButton::toggled, this, &MainWindow::updateFont); + connect(ui->rbGdi, &QRadioButton::toggled, this, &MainWindow::updateFont); + connect(ui->rbFreetype, &QRadioButton::toggled, this, &MainWindow::updateFont); + connect(ui->leText, &QLineEdit::textChanged, this, &MainWindow::updateFont); + connect(ui->cbWeight, &QComboBox::currentIndexChanged, this, &MainWindow::updateFont); + connect(ui->cbItalic, &QCheckBox::toggled, this, &MainWindow::updateFont); +} + +MainWindow::~MainWindow() +{ + delete ui; +} + +void MainWindow::updateImage() +{ + if (m_process == nullptr) + return; + + QImage img(QStringLiteral("output.png")); + if (!img.isNull()) + ui->lImage->setPixmap(QPixmap::fromImage(img)); +} + +void MainWindow::updateFont() +{ + if (m_process == nullptr) { + m_process = new QProcess; + connect(m_process, &QProcess::finished, this, &MainWindow::updateImage); + } + + if (m_process->isOpen()) + m_process->close(); + + QString fontEngineName = QStringLiteral("directwrite"); + if (ui->rbGdi->isChecked()) + fontEngineName = QStringLiteral("gdi"); + else if (ui->rbFreetype->isChecked()) + fontEngineName = QStringLiteral("freetype"); + + QProcessEnvironment env; + env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("windows:fontengine=%1").arg(fontEngineName)); + env.insert(QStringLiteral("windir"), qgetenv("windir")); + m_process->setProcessEnvironment(env); + + QStringList args; + args.append(ui->fontComboBox->currentFont().family()); + args.append(QString::number(ui->sbPixelSize->value())); + args.append(ui->leText->text().isEmpty() + ? QStringLiteral("The quick brown fox jumps over the lazy dog") + : ui->leText->text()); + args.append(QString::number(ui->cbWeight->currentData().toInt())); + args.append(QString::number(int(ui->cbItalic->isChecked()))); + + m_process->start(qApp->arguments().first(), args); + m_process->waitForFinished(); +} diff --git a/tests/manual/textrendering/fontenginecomparison/mainwindow.h b/tests/manual/textrendering/fontenginecomparison/mainwindow.h new file mode 100644 index 00000000000..a7a4545ddbb --- /dev/null +++ b/tests/manual/textrendering/fontenginecomparison/mainwindow.h @@ -0,0 +1,35 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class MainWindow; +} +QT_END_NAMESPACE + +class FontEngineRenderer; + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + MainWindow(QWidget *parent = nullptr); + ~MainWindow(); + +public slots: + void updateFont(); + void updateImage(); + +private: + Ui::MainWindow *ui; + FontEngineRenderer *m_renderer = nullptr; + QProcess *m_process = nullptr; +}; +#endif // MAINWINDOW_H diff --git a/tests/manual/textrendering/fontenginecomparison/mainwindow.ui b/tests/manual/textrendering/fontenginecomparison/mainwindow.ui new file mode 100644 index 00000000000..6393242740c --- /dev/null +++ b/tests/manual/textrendering/fontenginecomparison/mainwindow.ui @@ -0,0 +1,254 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + MainWindow + + + + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + Examples + + + + + + + + + + + + + + + + Text settings + + + + + + The quick brown fox jumps over the lazy dog + + + + + + + + + + + + + + + + 0 + 0 + + + + Italic + + + + + + + + + + + Pixel size: + + + + + + + 1 + + + 30 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Font engines + + + + + + Default + + + true + + + + + + + GDI + + + + + + + true + + + Freetype + + + false + + + + + + + + + + + + 0 + 0 + 800 + 22 + + + + + + + +