diff options
author | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com> | 2011-02-22 14:40:40 (GMT) |
---|---|---|
committer | Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@nokia.com> | 2011-03-14 08:25:42 (GMT) |
commit | c0fed43b04dec8bd549043d3ea5e28908128082c (patch) | |
tree | 53b111aa672e80585c32e5874646a2c93b186a76 | |
parent | 6f5553b95c4df489e0bf047399a90e9a564314e6 (diff) | |
download | Qt-c0fed43b04dec8bd549043d3ea5e28908128082c.zip Qt-c0fed43b04dec8bd549043d3ea5e28908128082c.tar.gz Qt-c0fed43b04dec8bd549043d3ea5e28908128082c.tar.bz2 |
Introduce QFontEngineDirectWrite
Make a font engine for subpixel positioned text on Windows Vista
(with platform update) and Windows 7. If selected during
configuration, the engine will be selected only when the hinting
preference of a font is set to None or Vertical hinting. The font
database uses most of the same logic but creates a direct write
font based on the LOGFONT rather than a GDI handle.
The engine is currently regarded as experimental, meaning that code
using it should do substantial testing to make sure it covers their
use cases.
Task-number: QTBUG-12678
Reviewed-by: Jiang Jiang
-rw-r--r-- | src/gui/gui.pro | 4 | ||||
-rw-r--r-- | src/gui/text/qfont.cpp | 102 | ||||
-rw-r--r-- | src/gui/text/qfont.h | 45 | ||||
-rw-r--r-- | src/gui/text/qfont_p.h | 7 | ||||
-rw-r--r-- | src/gui/text/qfontdatabase.cpp | 21 | ||||
-rw-r--r-- | src/gui/text/qfontdatabase_win.cpp | 220 | ||||
-rw-r--r-- | src/gui/text/qfontengine_p.h | 2 | ||||
-rw-r--r-- | src/gui/text/qfontengine_win.cpp | 2 | ||||
-rw-r--r-- | src/gui/text/qfontengine_win_p.h | 2 | ||||
-rw-r--r-- | src/gui/text/qfontenginedirectwrite.cpp | 646 | ||||
-rw-r--r-- | src/gui/text/qfontenginedirectwrite_p.h | 133 | ||||
-rw-r--r-- | src/gui/text/qtextformat.cpp | 22 | ||||
-rw-r--r-- | src/gui/text/qtextformat.h | 11 | ||||
-rw-r--r-- | src/gui/text/text.pri | 6 | ||||
-rw-r--r-- | tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp | 60 | ||||
-rw-r--r-- | tools/configure/configureapp.cpp | 34 |
16 files changed, 1259 insertions, 58 deletions
diff --git a/src/gui/gui.pro b/src/gui/gui.pro index 076fe0a..fda76a2 100644 --- a/src/gui/gui.pro +++ b/src/gui/gui.pro @@ -79,6 +79,10 @@ neon:*-g++* { QMAKE_EXTRA_COMPILERS += neon_compiler } +win32:!contains(QT_CONFIG, directwrite) { + DEFINES += QT_NO_DIRECTWRITE +} + contains(QMAKE_MAC_XARCH, no) { DEFINES += QT_NO_MAC_XARCH } else { diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index 64eb27a..f77e237 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -383,6 +383,9 @@ void QFontPrivate::resolve(uint mask, const QFontPrivate *other) if (! (mask & QFont::StretchResolved)) request.stretch = other->request.stretch; + if (! (mask & QFont::HintingPreferenceResolved)) + request.hintingPreference = other->request.hintingPreference; + if (! (mask & QFont::UnderlineResolved)) underline = other->underline; @@ -917,6 +920,105 @@ int QFont::pointSize() const } /*! + \since 4.8 + + \enum QFont::HintingPreference + + This enum describes the different levels of hinting that can be applied + to glyphs to improve legibility on displays where it might be warranted + by the density of pixels. + + \value PreferDefaultHinting Use the default hinting level for the target platform. + \value PreferNoHinting If possible, render text without hinting the outlines + of the glyphs. The text layout will be typographically accurate and + scalable, using the same metrics as are used e.g. when printing. + \value PreferVerticalHinting If possible, render text with no horizontal hinting, + but align glyphs to the pixel grid in the vertical direction. The text will appear + crisper on displays where the density is too low to give an accurate rendering + of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's + layout will be scalable to higher density devices (such as printers) without impacting + details such as line breaks. + \value PreferFullHinting If possible, render text with hinting in both horizontal and + vertical directions. The text will be altered to optimize legibility on the target + device, but since the metrics will depend on the target size of the text, the positions + of glyphs, line breaks, and other typographical detail will not scale, meaning that a + text layout may look different on devices with different pixel densities. + + Please note that this enum only describes a preference, as the full range of hinting levels + are not supported on all of Qt's supported platforms. The following table details the effect + of a given hinting preference on a selected set of target platforms. + + \table + \header + \o + \o PreferDefaultHinting + \o PreferNoHinting + \o PreferVerticalHinting + \o PreferFullHinting + \row + \o Windows Vista (w/o Platform Update) and earlier + \o Full hinting + \o Full hinting + \o Full hinting + \o Full hinting + \row + \o Windows 7 and Windows Vista (w/Platform Update) and DirectWrite enabled in Qt + \o Full hinting + \o Vertical hinting + \o Vertical hinting + \o Full hinting + \row + \o FreeType + \o Operating System setting + \o No hinting + \o Vertical hinting (light) + \o Full hinting + \row + \o Cocoa on Mac OS X + \o No hinting + \o No hinting + \o No hinting + \o No hinting + \endtable + + \note Please be aware that altering the hinting preference on Windows is available through + the DirectWrite font engine. This is available on Windows Vista after installing the platform + update, and on Windows 7. In order to use this extension, configure Qt using -directwrite. + The target application will then depend on the availability of DirectWrite on the target + system. + +*/ + +/*! + \since 4.8 + + Set the preference for the hinting level of the glyphs to \a hintingPreference. This is a hint + to the underlying font rendering system to use a certain level of hinting, and has varying + support across platforms. See the table in the documentation for QFont::HintingPreference for + more details. + + The default hinting preference is QFont::PreferDefaultHinting. +*/ +void QFont::setHintingPreference(HintingPreference hintingPreference) +{ + detach(); + + d->request.hintingPreference = hintingPreference; + + resolve_mask |= QFont::HintingPreferenceResolved; +} + +/*! + \since 4.8 + + Returns the currently preferred hinting level for glyphs rendered with this font. +*/ +QFont::HintingPreference QFont::hintingPreference() const +{ + return QFont::HintingPreference(d->request.hintingPreference); +} + +/*! Sets the point size to \a pointSize. The point size must be greater than zero. diff --git a/src/gui/text/qfont.h b/src/gui/text/qfont.h index e7e8a40..ea65c17 100644 --- a/src/gui/text/qfont.h +++ b/src/gui/text/qfont.h @@ -90,7 +90,14 @@ public: NoAntialias = 0x0100, OpenGLCompatible = 0x0200, ForceIntegerMetrics = 0x0400, - NoFontMerging = 0x8000 + NoFontMerging = 0x8000, + }; + + enum HintingPreference { + PreferDefaultHinting = 0, + PreferNoHinting = 1, + PreferVerticalHinting = 2, + PreferFullHinting = 3 }; enum Weight { @@ -133,22 +140,23 @@ public: }; enum ResolveProperties { - FamilyResolved = 0x0001, - SizeResolved = 0x0002, - StyleHintResolved = 0x0004, - StyleStrategyResolved = 0x0008, - WeightResolved = 0x0010, - StyleResolved = 0x0020, - UnderlineResolved = 0x0040, - OverlineResolved = 0x0080, - StrikeOutResolved = 0x0100, - FixedPitchResolved = 0x0200, - StretchResolved = 0x0400, - KerningResolved = 0x0800, - CapitalizationResolved = 0x1000, - LetterSpacingResolved = 0x2000, - WordSpacingResolved = 0x4000, - AllPropertiesResolved = 0x7fff + FamilyResolved = 0x0001, + SizeResolved = 0x0002, + StyleHintResolved = 0x0004, + StyleStrategyResolved = 0x0008, + WeightResolved = 0x0010, + StyleResolved = 0x0020, + UnderlineResolved = 0x0040, + OverlineResolved = 0x0080, + StrikeOutResolved = 0x0100, + FixedPitchResolved = 0x0200, + StretchResolved = 0x0400, + KerningResolved = 0x0800, + CapitalizationResolved = 0x1000, + LetterSpacingResolved = 0x2000, + WordSpacingResolved = 0x4000, + HintingPreferenceResolved = 0x8000, + AllPropertiesResolved = 0xffff }; QFont(); @@ -213,6 +221,9 @@ public: void setCapitalization(Capitalization); Capitalization capitalization() const; + void setHintingPreference(HintingPreference hintingPreference); + HintingPreference hintingPreference() const; + // is raw mode still needed? bool rawMode() const; void setRawMode(bool); diff --git a/src/gui/text/qfont_p.h b/src/gui/text/qfont_p.h index 0a3f76d..b23f96e 100644 --- a/src/gui/text/qfont_p.h +++ b/src/gui/text/qfont_p.h @@ -72,7 +72,7 @@ struct QFontDef : pointSize(-1.0), pixelSize(-1), styleStrategy(QFont::PreferDefault), styleHint(QFont::AnyStyle), weight(50), fixedPitch(false), style(QFont::StyleNormal), stretch(100), - ignorePitch(true) + ignorePitch(true), hintingPreference(QFont::PreferDefaultHinting) #ifdef Q_WS_MAC ,fixedPitchComputed(false) #endif @@ -98,7 +98,8 @@ struct QFontDef uint ignorePitch : 1; uint fixedPitchComputed : 1; // for Mac OS X only - int reserved : 16; // for future extensions + uint hintingPreference : 2; + int reserved : 14; // for future extensions bool exactMatch(const QFontDef &other) const; bool operator==(const QFontDef &other) const @@ -111,6 +112,7 @@ struct QFontDef && styleStrategy == other.styleStrategy && ignorePitch == other.ignorePitch && fixedPitch == other.fixedPitch && family == other.family + && hintingPreference == other.hintingPreference #ifdef Q_WS_X11 && addStyle == other.addStyle #endif @@ -125,6 +127,7 @@ struct QFontDef if (styleHint != other.styleHint) return styleHint < other.styleHint; if (styleStrategy != other.styleStrategy) return styleStrategy < other.styleStrategy; if (family != other.family) return family < other.family; + if (hintingPreference != other.hintingPreference) return hintingPreference < other.hintingPreference; #ifdef Q_WS_X11 if (addStyle != other.addStyle) return addStyle < other.addStyle; diff --git a/src/gui/text/qfontdatabase.cpp b/src/gui/text/qfontdatabase.cpp index 1e94bf5..cbe0423 100644 --- a/src/gui/text/qfontdatabase.cpp +++ b/src/gui/text/qfontdatabase.cpp @@ -81,6 +81,10 @@ # define FM_DEBUG if (false) qDebug #endif +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) +# include <dwrite.h> +#endif + QT_BEGIN_NAMESPACE #define SMOOTH_SCALABLE 0xffff @@ -643,13 +647,24 @@ public: #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) , symbianExtras(0) #endif +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) + , directWriteFactory(0) + , directWriteGdiInterop(0) +#endif { } + ~QFontDatabasePrivate() { free(); #if defined(Q_OS_SYMBIAN) && defined(QT_NO_FREETYPE) if (symbianExtras) delete symbianExtras; #endif +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) + if (directWriteGdiInterop) + directWriteGdiInterop->Release(); + if (directWriteFactory != 0) + directWriteFactory->Release(); +#endif } QtFontFamily *family(const QString &f, bool = false); void free() { @@ -667,6 +682,12 @@ public: #endif QtFontFamily **families; +#if defined(Q_WS_WIN) && !defined(QT_NO_DIRECTWRITE) + IDWriteFactory *directWriteFactory; + IDWriteGdiInterop *directWriteGdiInterop; +#endif + + struct ApplicationFont { QString fileName; QByteArray data; diff --git a/src/gui/text/qfontdatabase_win.cpp b/src/gui/text/qfontdatabase_win.cpp index e2c5116..0bb1d8d 100644 --- a/src/gui/text/qfontdatabase_win.cpp +++ b/src/gui/text/qfontdatabase_win.cpp @@ -49,6 +49,11 @@ #include "qabstractfileengine.h" #include "qendian.h" +#if !defined(QT_NO_DIRECTWRITE) +# include "qsettings.h" +# include "qfontenginedirectwrite_p.h" +#endif + #ifdef Q_OS_WINCE # include <QTemporaryFile> #endif @@ -542,6 +547,65 @@ static void initFontInfo(QFontEngineWin *fe, const QFontDef &request, const QFon } } +#if !defined(QT_NO_DIRECTWRITE) +static void initFontInfo(QFontEngineDirectWrite *fe, const QFontDef &request, + const QFontPrivate *fp, IDWriteFont *font) +{ + fe->fontDef = request; + + IDWriteFontFamily *fontFamily = NULL; + HRESULT hr = font->GetFontFamily(&fontFamily); + + IDWriteLocalizedStrings *familyNames = NULL; + if (SUCCEEDED(hr)) + hr = fontFamily->GetFamilyNames(&familyNames); + + UINT32 index = 0; + BOOL exists = false; + + wchar_t localeName[LOCALE_NAME_MAX_LENGTH]; + + if (SUCCEEDED(hr)) { + int defaultLocaleSuccess = GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH); + + if (defaultLocaleSuccess) + hr = familyNames->FindLocaleName(localeName, &index, &exists); + + if (SUCCEEDED(hr) && !exists) + hr = familyNames->FindLocaleName(L"en-us", &index, &exists); + } + + if (!exists) + index = 0; + + UINT32 length = 0; + if (SUCCEEDED(hr)) + hr = familyNames->GetStringLength(index, &length); + + wchar_t *name = new (std::nothrow) wchar_t[length+1]; + if (name == NULL) + hr = E_OUTOFMEMORY; + + // Get the family name. + if (SUCCEEDED(hr)) + hr = familyNames->GetString(index, name, length + 1); + + if (SUCCEEDED(hr)) + fe->fontDef.family = QString::fromWCharArray(name); + + delete[] name; + if (familyNames != NULL) + familyNames->Release(); + + if (FAILED(hr)) + qErrnoWarning(hr, "initFontInfo: Failed to get family name"); + + if (fe->fontDef.pointSize < 0) + fe->fontDef.pointSize = fe->fontDef.pixelSize * 72. / fp->dpi; + else if (fe->fontDef.pixelSize == -1) + fe->fontDef.pixelSize = qRound(fe->fontDef.pointSize * fp->dpi / 72.); +} +#endif static const char *other_tryFonts[] = { "Arial", @@ -595,6 +659,14 @@ static const char *kr_tryFonts[] = { static const char **tryFonts = 0; +#if !defined(QT_NO_DIRECTWRITE) +static QString fontNameSubstitute(const QString &familyName) +{ + QLatin1String key("HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\" + "FontSubstitutes"); + return QSettings(key, QSettings::NativeFormat).value(familyName, familyName).toString(); +} +#endif static inline HFONT systemFont() { @@ -629,6 +701,15 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ HFONT hfont = 0; + +#if !defined(QT_NO_DIRECTWRITE) + bool useDirectWrite = (request.hintingPreference & QFont::PreferNoHinting) + || (request.hintingPreference & QFont::PreferVerticalHinting); + IDWriteFont *directWriteFont = 0; +#else + bool useDirectWrite = false; +#endif + if (fp->rawMode) { // will choose a stock font int f, deffnt = SYSTEM_FONT; QString fam = desc->family->name.toLower(); @@ -745,6 +826,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ fam = QLatin1String("Courier New"); memcpy(lf.lfFaceName, fam.utf16(), sizeof(wchar_t) * qMin(fam.length() + 1, 32)); // 32 = Windows hard-coded + hfont = CreateFontIndirect(&lf); if (!hfont) qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect failed"); @@ -759,52 +841,120 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ res = GetTextMetrics(hdc, &tm); avWidth = tm.tmAveCharWidth; ttf = tm.tmPitchAndFamily & TMPF_TRUETYPE; - SelectObject(hdc, oldObj); - if (hfont && (!ttf || request.stretch != 100)) { - DeleteObject(hfont); - if (!res) - qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); - lf.lfWidth = avWidth * request.stretch/100; - hfont = CreateFontIndirect(&lf); - if (!hfont) - qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); - } + if (!ttf || !useDirectWrite) { + useDirectWrite = false; + + if (hfont && (!ttf || request.stretch != 100)) { + DeleteObject(hfont); + if (!res) + qErrnoWarning("QFontEngine::loadEngine: GetTextMetrics failed"); + lf.lfWidth = avWidth * request.stretch/100; + hfont = CreateFontIndirect(&lf); + if (!hfont) + qErrnoWarning("QFontEngine::loadEngine: CreateFontIndirect with stretch failed"); + } #ifndef Q_WS_WINCE - if (hfont == 0) { - hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); - stockFont = true; - } + if (hfont == 0) { + hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); + stockFont = true; + } #else - if (hfont == 0) { - hfont = (HFONT)GetStockObject(SYSTEM_FONT); - stockFont = true; + if (hfont == 0) { + hfont = (HFONT)GetStockObject(SYSTEM_FONT); + stockFont = true; + } +#endif + + } + +#if !defined(QT_NO_DIRECTWRITE) + else { + // Default to false for DirectWrite (and re-enable once/if everything + // turns out okay) + useDirectWrite = false; + + QFontDatabasePrivate *db = privateDb(); + if (db->directWriteFactory == 0) { + HRESULT hr = DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown **>(&db->directWriteFactory) + ); + if (FAILED(hr)) { + qErrnoWarning("QFontEngine::loadEngine: DWriteCreateFactory failed"); + } else { + hr = db->directWriteFactory->GetGdiInterop(&db->directWriteGdiInterop); + if (FAILED(hr)) + qErrnoWarning("QFontEngine::loadEngine: GetGdiInterop failed"); + } + } + + if (db->directWriteGdiInterop != 0) { + QString nameSubstitute = fontNameSubstitute(QString::fromWCharArray(lf.lfFaceName)); + memcpy(lf.lfFaceName, nameSubstitute.utf16(), + sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE)); + + HRESULT hr = db->directWriteGdiInterop->CreateFontFromLOGFONT( + &lf, + &directWriteFont); + if (FAILED(hr)) { + qErrnoWarning("QFontEngine::loadEngine: CreateFontFromLOGFONT failed " + "for %ls (0x%lx)", + lf.lfFaceName, hr); + } else { + DeleteObject(hfont); + useDirectWrite = true; + } + } } #endif } - QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf); - - if (preferClearTypeAA) - few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; - - // Also check for OpenType tables when using complex scripts - // ### TODO: This only works for scripts that require OpenType. More generally - // for scripts that do not require OpenType we should just look at the list of - // supported writing systems in the font's OS/2 table. - if (scriptRequiresOpenType(script)) { - HB_Face hbFace = few->harfbuzzFace(); - if (!hbFace || !hbFace->supported_scripts[script]) { - FM_DEBUG(" OpenType support missing for script\n"); - delete few; - return 0; + + QFontEngine *fe = 0; + if (!useDirectWrite) { + QFontEngineWin *few = new QFontEngineWin(font_name, hfont, stockFont, lf); + if (preferClearTypeAA) + few->glyphFormat = QFontEngineGlyphCache::Raster_RGBMask; + + // Also check for OpenType tables when using complex scripts + // ### TODO: This only works for scripts that require OpenType. More generally + // for scripts that do not require OpenType we should just look at the list of + // supported writing systems in the font's OS/2 table. + if (scriptRequiresOpenType(script)) { + HB_Face hbFace = few->harfbuzzFace(); + if (!hbFace || !hbFace->supported_scripts[script]) { + FM_DEBUG(" OpenType support missing for script\n"); + delete few; + return 0; + } } + + initFontInfo(few, request, fp); + fe = few; } - QFontEngine *fe = few; - initFontInfo(few, request, fp); +#if !defined(QT_NO_DIRECTWRITE) + else { + QFontDatabasePrivate *db = privateDb(); + QFontEngineDirectWrite *fedw = new QFontEngineDirectWrite(font_name, + db->directWriteFactory, + db->directWriteGdiInterop, + directWriteFont, + request.pixelSize); + + initFontInfo(fedw, request, fp, directWriteFont); + + fe = fedw; + } + + if (directWriteFont != 0) + directWriteFont->Release(); +#endif + if(script == QUnicodeTables::Common && !(request.styleStrategy & QFont::NoFontMerging) && !(desc->family->writingSystems[QFontDatabase::Symbol] & QtFontFamily::Supported)) { @@ -836,7 +986,7 @@ QFontEngine *loadEngine(int script, const QFontPrivate *fp, const QFontDef &requ list << QLatin1String(*tf); ++tf; } - QFontEngine *mfe = new QFontEngineMultiWin(few, list); + QFontEngine *mfe = new QFontEngineMultiWin(fe, list); mfe->fontDef = fe->fontDef; fe = mfe; } diff --git a/src/gui/text/qfontengine_p.h b/src/gui/text/qfontengine_p.h index bbb242b..f501141 100644 --- a/src/gui/text/qfontengine_p.h +++ b/src/gui/text/qfontengine_p.h @@ -117,6 +117,8 @@ public: // S60 types S60FontEngine, // Cannot be simply called "S60". Reason is qt_s60Data.h + DirectWrite, + TestFontEngine = 0x1000 }; diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp index ecb0384..82d9da0 100644 --- a/src/gui/text/qfontengine_win.cpp +++ b/src/gui/text/qfontengine_win.cpp @@ -1286,7 +1286,7 @@ QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, QFixed, int margin, co // -------------------------------------- Multi font engine -QFontEngineMultiWin::QFontEngineMultiWin(QFontEngineWin *first, const QStringList &fallbacks) +QFontEngineMultiWin::QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks) : QFontEngineMulti(fallbacks.size()+1), fallbacks(fallbacks) { diff --git a/src/gui/text/qfontengine_win_p.h b/src/gui/text/qfontengine_win_p.h index 22085e8..28d8000 100644 --- a/src/gui/text/qfontengine_win_p.h +++ b/src/gui/text/qfontengine_win_p.h @@ -150,7 +150,7 @@ private: class QFontEngineMultiWin : public QFontEngineMulti { public: - QFontEngineMultiWin(QFontEngineWin *first, const QStringList &fallbacks); + QFontEngineMultiWin(QFontEngine *first, const QStringList &fallbacks); void loadEngine(int at); QStringList fallbacks; diff --git a/src/gui/text/qfontenginedirectwrite.cpp b/src/gui/text/qfontenginedirectwrite.cpp new file mode 100644 index 0000000..23fff0d --- /dev/null +++ b/src/gui/text/qfontenginedirectwrite.cpp @@ -0,0 +1,646 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT_NO_DIRECTWRITE + +#include "qfontenginedirectwrite_p.h" + +#include <qendian.h> +#include <dwrite.h> +#include <private/qnativeimage_p.h> + +#include <d2d1.h> + +QT_BEGIN_NAMESPACE + +// Convert from design units to logical pixels +#define DESIGN_TO_LOGICAL(DESIGN_UNIT_VALUE) \ + QFixed::fromReal((qreal(DESIGN_UNIT_VALUE) / qreal(m_unitsPerEm)) * fontDef.pixelSize) + +namespace { + + class GeometrySink: public IDWriteGeometrySink + { + public: + GeometrySink(QPainterPath *path) : m_path(path), m_refCount(0) + { + Q_ASSERT(m_path != 0); + } + + IFACEMETHOD_(void, AddBeziers)(const D2D1_BEZIER_SEGMENT *beziers, UINT bezierCount); + IFACEMETHOD_(void, AddLines)(const D2D1_POINT_2F *points, UINT pointCount); + IFACEMETHOD_(void, BeginFigure)(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin); + IFACEMETHOD(Close)(); + IFACEMETHOD_(void, EndFigure)(D2D1_FIGURE_END figureEnd); + IFACEMETHOD_(void, SetFillMode)(D2D1_FILL_MODE fillMode); + IFACEMETHOD_(void, SetSegmentFlags)(D2D1_PATH_SEGMENT vertexFlags); + + IFACEMETHOD_(unsigned long, AddRef)(); + IFACEMETHOD_(unsigned long, Release)(); + IFACEMETHOD(QueryInterface)(IID const &riid, void **ppvObject); + + private: + inline static QPointF fromD2D1_POINT_2F(const D2D1_POINT_2F &inp) + { + return QPointF(inp.x, inp.y); + } + + unsigned long m_refCount; + QPointF m_startPoint; + QPainterPath *m_path; + }; + + void GeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, + UINT bezierCount) + { + for (uint i=0; i<bezierCount; ++i) { + QPointF c1 = fromD2D1_POINT_2F(beziers[i].point1); + QPointF c2 = fromD2D1_POINT_2F(beziers[i].point2); + QPointF p2 = fromD2D1_POINT_2F(beziers[i].point3); + + m_path->cubicTo(c1, c2, p2); + } + } + + void GeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) + { + for (uint i=0; i<pointsCount; ++i) + m_path->lineTo(fromD2D1_POINT_2F(points[i])); + } + + void GeometrySink::BeginFigure(D2D1_POINT_2F startPoint, + D2D1_FIGURE_BEGIN /*figureBegin*/) + { + m_startPoint = fromD2D1_POINT_2F(startPoint); + m_path->moveTo(m_startPoint); + } + + IFACEMETHODIMP GeometrySink::Close() + { + return E_NOTIMPL; + } + + void GeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) + { + if (figureEnd == D2D1_FIGURE_END_CLOSED) + m_path->closeSubpath(); + } + + void GeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) + { + m_path->setFillRule(fillMode == D2D1_FILL_MODE_ALTERNATE + ? Qt::OddEvenFill + : Qt::WindingFill); + } + + void GeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT /*vertexFlags*/) + { + /* Not implemented */ + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::AddRef() + { + return InterlockedIncrement(&m_refCount); + } + + IFACEMETHODIMP_(unsigned long) GeometrySink::Release() + { + unsigned long newCount = InterlockedDecrement(&m_refCount); + if (newCount == 0) + { + delete this; + return 0; + } + + return newCount; + } + + IFACEMETHODIMP GeometrySink::QueryInterface(IID const &riid, void **ppvObject) + { + if (__uuidof(IDWriteGeometrySink) == riid) { + *ppvObject = this; + } else if (__uuidof(IUnknown) == riid) { + *ppvObject = this; + } else { + *ppvObject = NULL; + return E_FAIL; + } + + AddRef(); + return S_OK; + } + +} + +QFontEngineDirectWrite::QFontEngineDirectWrite(const QString &name, + IDWriteFactory *directWriteFactory, + IDWriteGdiInterop *directWriteGdiInterop, + IDWriteFont *directWriteFont, + qreal pixelSize) + : m_name(name) + , m_directWriteFont(directWriteFont) + , m_directWriteFontFace(0) + , m_directWriteFactory(directWriteFactory) + , m_directWriteBitmapRenderTarget(0) + , m_directWriteGdiInterop(directWriteGdiInterop) + , m_lineThickness(-1) + , m_unitsPerEm(-1) + , m_ascent(-1) + , m_descent(-1) + , m_xHeight(-1) + , m_lineGap(-1) +{ + m_directWriteFont->AddRef(); + m_directWriteFactory->AddRef(); + m_directWriteGdiInterop->AddRef(); + + fontDef.pixelSize = pixelSize; + + HRESULT hr = m_directWriteFont->CreateFontFace(&m_directWriteFontFace); + if (FAILED(hr)) + qErrnoWarning("QFontEngineDirectWrite: CreateFontFace failed"); + + collectMetrics(); +} + +QFontEngineDirectWrite::~QFontEngineDirectWrite() +{ + m_directWriteFont->Release(); + m_directWriteFactory->Release(); + m_directWriteGdiInterop->Release(); + + if (m_directWriteBitmapRenderTarget != 0) + m_directWriteBitmapRenderTarget->Release(); +} + +void QFontEngineDirectWrite::collectMetrics() +{ + if (m_directWriteFont != 0) { + DWRITE_FONT_METRICS metrics; + + m_directWriteFont->GetMetrics(&metrics); + m_unitsPerEm = metrics.designUnitsPerEm; + + m_lineThickness = DESIGN_TO_LOGICAL(metrics.underlineThickness); + m_ascent = DESIGN_TO_LOGICAL(metrics.ascent); + m_descent = DESIGN_TO_LOGICAL(metrics.descent); + m_xHeight = DESIGN_TO_LOGICAL(metrics.xHeight); + m_lineGap = DESIGN_TO_LOGICAL(metrics.lineGap); + } +} + +QFixed QFontEngineDirectWrite::lineThickness() const +{ + if (m_lineThickness > 0) + return m_lineThickness; + else + return QFontEngine::lineThickness(); +} + +bool QFontEngineDirectWrite::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + if (m_directWriteFontFace) { + DWORD t = qbswap<quint32>(tag); + + const void *tableData = 0; + void *tableContext = 0; + UINT32 tableSize; + BOOL exists; + HRESULT hr = m_directWriteFontFace->TryGetFontTable( + t, &tableData, &tableSize, &tableContext, &exists + ); + + if (SUCCEEDED(hr)) { + if (!exists) + return false; + + if (buffer == 0) { + *length = tableSize; + return true; + } else if (*length < tableSize) { + return false; + } + + qMemCopy(buffer, tableData, tableSize); + m_directWriteFontFace->ReleaseFontTable(tableContext); + + return true; + } else { + qErrnoWarning("QFontEngineDirectWrite::getSfntTableData: TryGetFontTable failed"); + } + } + + return false; +} + +QFixed QFontEngineDirectWrite::emSquareSize() const +{ + if (m_unitsPerEm > 0) + return m_unitsPerEm; + else + return QFontEngine::emSquareSize(); +} + +inline unsigned int getChar(const QChar *str, int &i, const int len) +{ + unsigned int uc = str[i].unicode(); + if (uc >= 0xd800 && uc < 0xdc00 && i < len-1) { + uint low = str[i+1].unicode(); + if (low >= 0xdc00 && low < 0xe000) { + uc = (uc - 0xd800)*0x400 + (low - 0xdc00) + 0x10000; + ++i; + } + } + return uc; +} + +bool QFontEngineDirectWrite::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, + int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (m_directWriteFontFace != 0) { + QVarLengthArray<UINT32> codePoints(len); + for (int i=0; i<len; ++i) { + codePoints[i] = getChar(str, i, len); + if (flags & QTextEngine::RightToLeft) + codePoints[i] = QChar::mirroredChar(codePoints[i]); + } + + QVarLengthArray<UINT16> glyphIndices(len); + HRESULT hr = m_directWriteFontFace->GetGlyphIndicesW(codePoints.data(), + len, + glyphIndices.data()); + + if (SUCCEEDED(hr)) { + for (int i=0; i<len; ++i) + glyphs->glyphs[i] = glyphIndices[i]; + + *nglyphs = len; + + if (!(flags & QTextEngine::GlyphIndicesOnly)) + recalcAdvances(glyphs, 0); + + return true; + } else { + qErrnoWarning("QFontEngineDirectWrite::stringToCMap: GetGlyphIndicesW failed"); + } + } + + return false; +} + +void QFontEngineDirectWrite::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const +{ + if (m_directWriteFontFace == 0) + return; + + QVarLengthArray<UINT16> glyphIndices(glyphs->numGlyphs); + + // ### Caching? + for(int i=0; i<glyphs->numGlyphs; i++) + glyphIndices[i] = UINT16(glyphs->glyphs[i]); + + QVarLengthArray<DWRITE_GLYPH_METRICS> glyphMetrics(glyphIndices.size()); + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(glyphIndices.data(), + glyphIndices.size(), + glyphMetrics.data()); + if (SUCCEEDED(hr)) { + for (int i=0; i<glyphs->numGlyphs; ++i) { + glyphs->advances_x[i] = DESIGN_TO_LOGICAL(glyphMetrics[i].advanceWidth); + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) + glyphs->advances_x[i] = glyphs->advances_x[i].round(); + glyphs->advances_y[i] = 0; + } + } else { + qErrnoWarning("QFontEngineDirectWrite::recalcAdvances: GetDesignGlyphMetrics failed"); + } +} + +void QFontEngineDirectWrite::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags) +{ + if (m_directWriteFontFace == 0) + return; + + QVarLengthArray<UINT16> glyphIndices(nglyphs); + QVarLengthArray<DWRITE_GLYPH_OFFSET> glyphOffsets(nglyphs); + QVarLengthArray<FLOAT> glyphAdvances(nglyphs); + + for (int i=0; i<nglyphs; ++i) { + glyphIndices[i] = glyphs[i]; + glyphOffsets[i].advanceOffset = positions[i].x.toReal(); + glyphOffsets[i].ascenderOffset = -positions[i].y.toReal(); + glyphAdvances[i] = 0.0; + } + + GeometrySink geometrySink(path); + HRESULT hr = m_directWriteFontFace->GetGlyphRunOutline( + fontDef.pixelSize, + glyphIndices.data(), + glyphAdvances.data(), + glyphOffsets.data(), + nglyphs, + false, + flags & QTextItem::RightToLeft, + &geometrySink + ); + + if (FAILED(hr)) + qErrnoWarning("QFontEngineDirectWrite::addGlyphsToPath: GetGlyphRunOutline failed"); +} + +glyph_metrics_t QFontEngineDirectWrite::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + bool round = fontDef.styleStrategy & QFont::ForceIntegerMetrics; + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) { + w += round ? glyphs.effectiveAdvance(i).round() : glyphs.effectiveAdvance(i); + + } + + return glyph_metrics_t(0, -m_ascent, w - lastRightBearing(glyphs), m_ascent + m_descent, w, 0); +} + +glyph_metrics_t QFontEngineDirectWrite::boundingBox(glyph_t g) +{ + if (m_directWriteFontFace == 0) + return glyph_metrics_t(); + + UINT16 glyphIndex = g; + + DWRITE_GLYPH_METRICS glyphMetrics; + HRESULT hr = m_directWriteFontFace->GetDesignGlyphMetrics(&glyphIndex, 1, &glyphMetrics); + if (SUCCEEDED(hr)) { + QFixed advanceWidth = DESIGN_TO_LOGICAL(glyphMetrics.advanceWidth); + QFixed leftSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.leftSideBearing); + QFixed rightSideBearing = DESIGN_TO_LOGICAL(glyphMetrics.rightSideBearing); + QFixed advanceHeight = DESIGN_TO_LOGICAL(glyphMetrics.advanceHeight); + QFixed verticalOriginY = DESIGN_TO_LOGICAL(glyphMetrics.verticalOriginY); + + if (fontDef.styleStrategy & QFont::ForceIntegerMetrics) { + advanceWidth = advanceWidth.round(); + advanceHeight = advanceHeight.round(); + } + + QFixed width = advanceWidth - leftSideBearing - rightSideBearing; + + return glyph_metrics_t(-leftSideBearing, -verticalOriginY, + width, m_ascent + m_descent, + advanceWidth, advanceHeight); + } else { + qErrnoWarning("QFontEngineDirectWrite::boundingBox: GetDesignGlyphMetrics failed"); + } + + return glyph_metrics_t(); +} + +QFixed QFontEngineDirectWrite::ascent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_ascent.round() + : m_ascent; +} + +QFixed QFontEngineDirectWrite::descent() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? (m_descent - 1).round() + : (m_descent - 1); +} + +QFixed QFontEngineDirectWrite::leading() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_lineGap.round() + : m_lineGap; +} + +QFixed QFontEngineDirectWrite::xHeight() const +{ + return fontDef.styleStrategy & QFont::ForceIntegerMetrics + ? m_xHeight.round() + : m_xHeight; +} + +qreal QFontEngineDirectWrite::maxCharWidth() const +{ + // ### + return 0; +} + +extern uint qt_pow_gamma[256]; + +QImage QFontEngineDirectWrite::alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition) +{ + QImage im = imageForGlyph(glyph, subPixelPosition, 0, QTransform()); + + QImage indexed(im.width(), im.height(), QImage::Format_Indexed8); + QVector<QRgb> colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + indexed.setColorTable(colors); + + for (int y=0; y<im.height(); ++y) { + uint *src = (uint*) im.scanLine(y); + uchar *dst = indexed.scanLine(y); + for (int x=0; x<im.width(); ++x) { + *dst = qGray(*src); + if (QNativeImage::systemFormat() == QImage::Format_RGB16) + *dst = 255 - qGray(*src); + else + *dst = 255 - (qt_pow_gamma[qGray(*src)] * 255. / 2047.); + + ++dst; + ++src; + } + } + + return indexed; +} + +bool QFontEngineDirectWrite::supportsSubPixelPositions() const +{ + return true; +} + +QImage QFontEngineDirectWrite::imageForGlyph(glyph_t t, + QFixed subPixelPosition, + int margin, + const QTransform &xform) +{ + glyph_metrics_t metrics = QFontEngine::boundingBox(t, xform); + int width = (metrics.width + margin * 2 + 4).ceil().toInt() ; + int height = (metrics.height + margin * 2 + 4).ceil().toInt(); + + UINT16 glyphIndex = t; + FLOAT glyphAdvance = metrics.xoff.toReal(); + + DWRITE_GLYPH_OFFSET glyphOffset; + glyphOffset.advanceOffset = 0; + glyphOffset.ascenderOffset = 0; + + DWRITE_GLYPH_RUN glyphRun; + glyphRun.fontFace = m_directWriteFontFace; + glyphRun.fontEmSize = fontDef.pixelSize; + glyphRun.glyphCount = 1; + glyphRun.glyphIndices = &glyphIndex; + glyphRun.glyphAdvances = &glyphAdvance; + glyphRun.isSideways = false; + glyphRun.bidiLevel = 0; + glyphRun.glyphOffsets = &glyphOffset; + + QFixed x = margin - metrics.x.round() + subPixelPosition; + QFixed y = margin - metrics.y.floor(); + + DWRITE_MATRIX transform; + transform.dx = x.toReal(); + transform.dy = y.toReal(); + transform.m11 = xform.m11(); + transform.m12 = xform.m12(); + transform.m21 = xform.m21(); + transform.m22 = xform.m22(); + + IDWriteGlyphRunAnalysis *glyphAnalysis = NULL; + HRESULT hr = m_directWriteFactory->CreateGlyphRunAnalysis( + &glyphRun, + 1.0f, + &transform, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0.0, 0.0, + &glyphAnalysis + ); + + if (SUCCEEDED(hr)) { + RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = width; + rect.bottom = height; + + int size = width * height * 3; + BYTE *alphaValues = new BYTE[size]; + qMemSet(alphaValues, size, 0); + + hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, + &rect, + alphaValues, + size); + + if (SUCCEEDED(hr)) { + QImage img(width, height, QImage::Format_RGB32); + img.fill(0xffffffff); + + for (int y=0; y<height; ++y) { + uint *dest = reinterpret_cast<uint *>(img.scanLine(y)); + BYTE *src = alphaValues + width * 3 * y; + + for (int x=0; x<width; ++x) { + dest[x] = *(src) << 16 + | *(src + 1) << 8 + | *(src + 2); + + src += 3; + } + } + + delete[] alphaValues; + glyphAnalysis->Release(); + + return img; + } else { + delete[] alphaValues; + glyphAnalysis->Release(); + + qErrnoWarning("QFontEngineDirectWrite::imageForGlyph: CreateAlphaTexture failed"); + } + + } else { + qErrnoWarning("QFontEngineDirectWrite::imageForGlyph: CreateGlyphRunAnalysis failed"); + } + + return QImage(); +} + +QImage QFontEngineDirectWrite::alphaRGBMapForGlyph(glyph_t t, + QFixed subPixelPosition, + int margin, + const QTransform &xform) +{ + QImage mask = imageForGlyph(t, subPixelPosition, margin, xform); + return mask.depth() == 32 + ? mask + : mask.convertToFormat(QImage::Format_RGB32); +} + +const char *QFontEngineDirectWrite::name() const +{ + return 0; +} + +bool QFontEngineDirectWrite::canRender(const QChar *string, int len) +{ + for (int i=0; i<len; ++i) { + BOOL exists; + UINT32 codePoint = getChar(string, i, len); + HRESULT hr = m_directWriteFont->HasCharacter(codePoint, &exists); + if (FAILED(hr)) { + qErrnoWarning("QFontEngineDirectWrite::canRender: HasCharacter failed"); + return false; + } else if (!exists) { + return false; + } + } + + return true; +} + +QFontEngine::Type QFontEngineDirectWrite::type() const +{ + return QFontEngine::DirectWrite; +} + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE diff --git a/src/gui/text/qfontenginedirectwrite_p.h b/src/gui/text/qfontenginedirectwrite_p.h new file mode 100644 index 0000000..80f90b8 --- /dev/null +++ b/src/gui/text/qfontenginedirectwrite_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFONTENGINEDIRECTWRITE_H +#define QFONTENGINEDIRECTWRITE_H + +#ifndef QT_NO_DIRECTWRITE + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "private/qfontengine_p.h" + +struct IDWriteFont ; +struct IDWriteFontFace ; +struct IDWriteFactory ; +struct IDWriteBitmapRenderTarget ; +struct IDWriteGdiInterop ; + +QT_BEGIN_NAMESPACE + +class QFontEngineDirectWrite : public QFontEngine +{ + Q_OBJECT +public: + explicit QFontEngineDirectWrite(const QString &name, + IDWriteFactory *directWriteFactory, + IDWriteGdiInterop *directWriteGdiInterop, + IDWriteFont *directWriteFont, + qreal pixelSize); + ~QFontEngineDirectWrite(); + + QFixed lineThickness() const; + bool getSfntTableData(uint tag, uchar *buffer, uint *length) const; + QFixed emSquareSize() const; + + bool stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const; + void recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags) const; + + void addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags flags); + + glyph_metrics_t boundingBox(const QGlyphLayout &glyphs); + glyph_metrics_t boundingBox(glyph_t g); + + QFixed ascent() const; + QFixed descent() const; + QFixed leading() const; + QFixed xHeight() const; + qreal maxCharWidth() const; + + const char *name() const; + + bool supportsSubPixelPositions() const; + + QImage alphaMapForGlyph(glyph_t glyph, QFixed subPixelPosition); + QImage alphaRGBMapForGlyph(glyph_t t, QFixed subPixelPosition, int margin, + const QTransform &xform); + + bool canRender(const QChar *string, int len); + Type type() const; + +private: + QImage imageForGlyph(glyph_t t, QFixed subPixelPosition, int margin, const QTransform &xform); + void collectMetrics(); + + QString m_name; + IDWriteFont *m_directWriteFont; + IDWriteFontFace *m_directWriteFontFace; + IDWriteFactory *m_directWriteFactory; + IDWriteBitmapRenderTarget *m_directWriteBitmapRenderTarget; + IDWriteGdiInterop *m_directWriteGdiInterop; + + QFixed m_lineThickness; + int m_unitsPerEm; + QFixed m_ascent; + QFixed m_descent; + QFixed m_xHeight; + QFixed m_lineGap; + FaceId m_faceId; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_DIRECTWRITE + +#endif // QFONTENGINEDIRECTWRITE_H diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp index c9af401..a02ea49 100644 --- a/src/gui/text/qtextformat.cpp +++ b/src/gui/text/qtextformat.cpp @@ -411,6 +411,9 @@ void QTextFormatPrivate::recalcFont() const case QTextFormat::FontStyleHint: f.setStyleHint(static_cast<QFont::StyleHint>(props.at(i).value.toInt()), f.styleStrategy()); break; + case QTextFormat::FontHintingPreference: + f.setHintingPreference(static_cast<QFont::HintingPreference>(props.at(i).value.toInt())); + break; case QTextFormat::FontStyleStrategy: f.setStyleStrategy(static_cast<QFont::StyleStrategy>(props.at(i).value.toInt())); break; @@ -1565,6 +1568,25 @@ void QTextCharFormat::setUnderlineStyle(UnderlineStyle style) \sa font() */ +/*! + \since 4.8 + + \fn void QTextCharFormat::setFontHintingPreference(QFont::HintingPreference hintingPreference) + + Sets the hinting preference of the text format's font to be \a hintingPreference. + + \sa setFont(), QFont::setHintingPreference() +*/ + +/*! + \since 4.8 + + \fn QFont::HintingPreference QTextCharFormat::fontHintingPreference() const + + Returns the hinting preference set for this text format. + + \sa font(), QFont::hintingPreference() +*/ /*! \fn QPen QTextCharFormat::textOutline() const diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h index 9a573a0..ff28eaa 100644 --- a/src/gui/text/qtextformat.h +++ b/src/gui/text/qtextformat.h @@ -177,6 +177,7 @@ public: FontStyleHint = 0x1FE3, FontStyleStrategy = 0x1FE4, FontKerning = 0x1FE5, + FontHintingPreference = 0x1FE6, FontFamily = 0x2000, FontPointSize = 0x2001, FontSizeAdjustment = 0x2002, @@ -460,6 +461,16 @@ public: QFont::StyleStrategy fontStyleStrategy() const { return static_cast<QFont::StyleStrategy>(intProperty(FontStyleStrategy)); } + inline void setFontHintingPreference(QFont::HintingPreference hintingPreference) + { + setProperty(FontHintingPreference, hintingPreference); + } + + inline QFont::HintingPreference fontHintingPreference() const + { + return static_cast<QFont::HintingPreference>(intProperty(FontHintingPreference)); + } + inline void setFontKerning(bool enable) { setProperty(FontKerning, enable); } inline bool fontKerning() const diff --git a/src/gui/text/text.pri b/src/gui/text/text.pri index d3e8f2d..7fb2783 100644 --- a/src/gui/text/text.pri +++ b/src/gui/text/text.pri @@ -81,6 +81,12 @@ win32 { HEADERS += text/qfontengine_win_p.h } +contains(QT_CONFIG, directwrite) { + LIBS_PRIVATE += -ldwrite + HEADERS += text/qfontenginedirectwrite_p.h + SOURCES += text/qfontenginedirectwrite.cpp +} + unix:x11 { HEADERS += \ text/qfontengine_x11_p.h \ diff --git a/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp b/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp index 4f4e706a..07da68d 100644 --- a/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp +++ b/tests/auto/qtextscriptengine/tst_qtextscriptengine.cpp @@ -61,6 +61,9 @@ #include <private/qtextengine_p.h> #include <qtextlayout.h> #undef private +#else +#include <private/qtextengine_p.h> +#include <qtextlayout.h> #endif #include <qfontdatabase.h> @@ -105,6 +108,9 @@ private slots: void linearB(); void controlInSyllable_qtbug14204(); void combiningMarks_qtbug15675(); + + void mirroredChars_data(); + void mirroredChars(); }; tst_QTextScriptEngine::tst_QTextScriptEngine() @@ -1156,5 +1162,59 @@ void tst_QTextScriptEngine::combiningMarks_qtbug15675() #endif } +void tst_QTextScriptEngine::mirroredChars_data() +{ + QTest::addColumn<int>("hintingPreference"); + + QTest::newRow("Default hinting") << int(QFont::PreferDefaultHinting); + QTest::newRow("No hinting") << int(QFont::PreferNoHinting); + QTest::newRow("Vertical hinting") << int(QFont::PreferVerticalHinting); + QTest::newRow("Full hinting") << int(QFont::PreferFullHinting); +} + +void tst_QTextScriptEngine::mirroredChars() +{ + QFETCH(int, hintingPreference); + + QFont font; + font.setHintingPreference(QFont::HintingPreference(hintingPreference)); + + QString s; + s.append(QLatin1Char('(')); + s.append(QLatin1Char(')')); + + HB_Glyph leftParenthesis; + HB_Glyph rightParenthesis; + { + QTextLayout layout(s); + layout.beginLayout(); + layout.createLine(); + layout.endLayout(); + + QTextEngine *e = layout.engine(); + e->itemize(); + e->shape(0); + QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(2)); + + const QGlyphLayout &glyphLayout = e->layoutData->glyphLayout; + leftParenthesis = glyphLayout.glyphs[0]; + rightParenthesis = glyphLayout.glyphs[1]; + } + + { + QTextLayout layout(s); + layout.setFlags(Qt::TextForceRightToLeft); + + QTextEngine *e = layout.engine(); + e->itemize(); + e->shape(0); + QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(2)); + + const QGlyphLayout &glyphLayout = e->layoutData->glyphLayout; + QCOMPARE(glyphLayout.glyphs[0], rightParenthesis); + QCOMPARE(glyphLayout.glyphs[1], leftParenthesis); + } +} + QTEST_MAIN(tst_QTextScriptEngine) #include "tst_qtextscriptengine.moc" diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 19cef9a..4503235 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -281,6 +281,7 @@ Configure::Configure(int& argc, char** argv) dictionary[ "DECLARATIVE" ] = "auto"; dictionary[ "DECLARATIVE_DEBUG" ]= "yes"; dictionary[ "PLUGIN_MANIFESTS" ] = "yes"; + dictionary[ "DIRECTWRITE" ] = "no"; QString version; QFile qglobal_h(sourcePath + "/src/corelib/global/qglobal.h"); @@ -1229,6 +1230,12 @@ void Configure::parseCmdLine() } } + else if (configCmdLine.at(i) == "-directwrite") { + dictionary["DIRECTWRITE"] = "yes"; + } else if (configCmdLine.at(i) == "-no-directwrite") { + dictionary["DIRECTWRITE"] = "no"; + } + else { dictionary[ "HELP" ] = "yes"; cout << "Unknown option " << configCmdLine.at(i) << endl; @@ -1684,7 +1691,9 @@ bool Configure::displayHelp() "[-phonon] [-no-phonon-backend] [-phonon-backend]\n" "[-no-multimedia] [-multimedia] [-no-audio-backend] [-audio-backend]\n" "[-no-script] [-script] [-no-scripttools] [-scripttools]\n" - "[-no-webkit] [-webkit] [-webkit-debug] [-graphicssystem raster|opengl|openvg]\n\n", 0, 7); + "[-no-webkit] [-webkit] [-webkit-debug]\n" + "[-graphicssystem raster|opengl|openvg]\n" + "[-no-directwrite] [-directwrite]\n\n", 0, 7); desc("Installation options:\n\n"); @@ -1881,6 +1890,8 @@ bool Configure::displayHelp() desc("DECLARATIVE", "yes", "-declarative", "Build the declarative module"); desc("DECLARATIVE_DEBUG", "no", "-no-declarative-debug", "Do not build the declarative debugging support"); desc("DECLARATIVE_DEBUG", "yes", "-declarative-debug", "Build the declarative debugging support"); + desc("DIRECTWRITE", "no", "-no-directwrite", "Do not build support for DirectWrite font rendering"); + desc("DIRECTWRITE", "yes", "-directwrite", "Build support for DirectWrite font rendering (experimental, requires DirectWrite availability on target systems, e.g. Windows Vista with Platform Update, Windows 7, etc.)"); desc( "-arch <arch>", "Specify an architecture.\n" "Available values for <arch>:"); @@ -2244,6 +2255,8 @@ bool Configure::checkAvailability(const QString &part) available = false; } } + } else if (part == "DIRECTWRITE") { + available = findFile("dwrite.h") && findFile("d2d1.h") && findFile("dwrite.lib"); } return available; @@ -2399,6 +2412,15 @@ bool Configure::verifyConfiguration() dictionary["SCRIPT"] = "yes"; } + if (dictionary["DIRECTWRITE"] == "yes" && !checkAvailability("DIRECTWRITE")) { + cout << "WARNING: To be able to compile the DirectWrite font engine you will" << endl + << "need the Microsoft DirectWrite and Microsoft Direct2D development" << endl + << "files such as headers and libraries." << endl + << "(Press any key to continue..)"; + if (_getch() == 3) // _Any_ keypress w/no echo(eat <Enter> for stdout) + exit(0); // Exit cleanly for Ctrl+C + } + return true; } @@ -2755,6 +2777,9 @@ void Configure::generateOutputVars() qtConfig += "declarative"; } + if (dictionary["DIRECTWRITE"] == "yes") + qtConfig += "directwrite"; + if (dictionary[ "NATIVE_GESTURES" ] == "yes") qtConfig += "native-gestures"; @@ -2986,6 +3011,10 @@ void Configure::generateCachefile() configStream << " def_files_disabled"; } } + + if (dictionary["DIRECTWRITE"] == "yes") + configStream << "directwrite"; + configStream << endl; configStream << "QT_ARCH = " << dictionary[ "ARCHITECTURE" ] << endl; if (dictionary["QT_EDITION"].contains("OPENSOURCE")) @@ -3454,7 +3483,8 @@ void Configure::displayConfig() cout << "QtScript support............" << dictionary[ "SCRIPT" ] << endl; cout << "QtScriptTools support......." << dictionary[ "SCRIPTTOOLS" ] << endl; cout << "Graphics System............." << dictionary[ "GRAPHICS_SYSTEM" ] << endl; - cout << "Qt3 compatibility..........." << dictionary[ "QT3SUPPORT" ] << endl << endl; + cout << "Qt3 compatibility..........." << dictionary[ "QT3SUPPORT" ] << endl; + cout << "DirectWrite support........." << dictionary[ "DIRECTWRITE" ] << endl << endl; cout << "Third Party Libraries:" << endl; cout << " ZLIB support............" << dictionary[ "ZLIB" ] << endl; |