diff options
6 files changed, 479 insertions, 19 deletions
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<DWRITE_GLYPH_METRICS> 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 <QtGui> +#include <QApplication> + +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 <QProcess> + +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 <QMainWindow> +#include <QProcess> + +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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="palette"> + <palette> + <active> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="windowTitle"> + <string>MainWindow</string> + </property> + <widget class="QWidget" name="centralwidget"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="gbExamples"> + <property name="palette"> + <palette> + <active> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </active> + <inactive> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </inactive> + <disabled> + <colorrole role="Base"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + <colorrole role="Window"> + <brush brushstyle="SolidPattern"> + <color alpha="255"> + <red>255</red> + <green>255</green> + <blue>255</blue> + </color> + </brush> + </colorrole> + </disabled> + </palette> + </property> + <property name="title"> + <string>Examples</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLabel" name="lImage"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Text settings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLineEdit" name="leText"> + <property name="text"> + <string>The quick brown fox jumps over the lazy dog</string> + </property> + </widget> + </item> + <item> + <widget class="QFontComboBox" name="fontComboBox"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QComboBox" name="cbWeight"/> + </item> + <item> + <widget class="QCheckBox" name="cbItalic"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Italic</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Pixel size:</string> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="sbPixelSize"> + <property name="minimum"> + <number>1</number> + </property> + <property name="value"> + <number>30</number> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Orientation::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Font engines</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QRadioButton" name="rbDefault"> + <property name="text"> + <string>Default</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="rbGdi"> + <property name="text"> + <string>GDI</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="rbFreetype"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Freetype</string> + </property> + <property name="checked"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QMenuBar" name="menubar"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>22</height> + </rect> + </property> + </widget> + <widget class="QStatusBar" name="statusbar"/> + </widget> + <resources/> + <connections/> +</ui> |
