Fix QGlyphRun text rendering

When drawing QGlyphRun objects through QPainter the QFont passed in
QStaticTextItem/QTextItem is not properly initialized with the correct
size, weight, style strategy etc. Shuffle things around so we always go
through QFontEngine for font data, as that should be more reliable.

Change-Id: I43811c868ebd4fb1d9e937ee28a6d637267b4c7f
Reviewed-by: Risto Avila <risto.avila@digia.com>
Reviewed-by: Andrew Knight <andrew.knight@digia.com>
This commit is contained in:
Louai Al-Khanji 2014-08-18 12:38:42 +03:00
parent 6ebc0bd863
commit 98cd256d24
2 changed files with 95 additions and 129 deletions

View File

@ -48,11 +48,11 @@
#include "qwindowsdirect2ddevicecontext.h"
#include "qwindowsfontengine.h"
#include "qwindowsfontenginedirectwrite.h"
#include "qwindowsfontdatabase.h"
#include "qwindowsintegration.h"
#include <QtCore/QStack>
#include <QtCore/QSettings>
#include <QtGui/private/qpaintengine_p.h>
#include <QtGui/private/qtextengine_p.h>
#include <QtGui/private/qfontengine_p.h>
@ -109,6 +109,13 @@ static inline ID2D1Factory1 *factory()
return QWindowsDirect2DContext::instance()->d2dFactory();
}
inline static FLOAT pixelSizeToDIP(int pixelSize)
{
FLOAT dpiX, dpiY;
QWindowsDirect2DContext::instance()->d2dFactory()->GetDesktopDpi(&dpiX, &dpiY);
return FLOAT(pixelSize) * 96.0f / dpiY;
}
class Direct2DPathGeometryWriter
{
public:
@ -243,7 +250,7 @@ public:
QPointF currentBrushOrigin;
QHash< QFont, ComPtr<IDWriteFontFace> > fontCache;
QHash< QFontDef, ComPtr<IDWriteFontFace> > fontCache;
struct {
bool emulate;
@ -836,6 +843,74 @@ public:
{
dc()->SetAntialiasMode(antialiasMode());
}
void drawGlyphRun(const D2D1_POINT_2F &pos,
IDWriteFontFace *fontFace,
const QFontDef &fontDef,
int numGlyphs,
const UINT16 *glyphIndices,
const FLOAT *glyphAdvances,
const DWRITE_GLYPH_OFFSET *glyphOffsets,
bool rtl)
{
Q_Q(QWindowsDirect2DPaintEngine);
DWRITE_GLYPH_RUN glyphRun = {
fontFace, // IDWriteFontFace *fontFace;
pixelSizeToDIP(fontDef.pixelSize), // FLOAT fontEmSize;
numGlyphs, // UINT32 glyphCount;
glyphIndices, // const UINT16 *glyphIndices;
glyphAdvances, // const FLOAT *glyphAdvances;
glyphOffsets, // const DWRITE_GLYPH_OFFSET *glyphOffsets;
FALSE, // BOOL isSideways;
rtl ? 1 : 0 // UINT32 bidiLevel;
};
const bool antiAlias = bool((q->state()->renderHints & QPainter::TextAntialiasing)
&& !(fontDef.styleStrategy & QFont::NoAntialias));
dc()->SetTextAntialiasMode(antiAlias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
: D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
dc()->DrawGlyphRun(pos,
&glyphRun,
NULL,
pen.brush.Get(),
DWRITE_MEASURING_MODE_GDI_CLASSIC);
}
ComPtr<IDWriteFontFace> fontFaceFromFontEngine(QFontEngine *fe)
{
const QFontDef fontDef = fe->fontDef;
ComPtr<IDWriteFontFace> fontFace = fontCache.value(fontDef);
if (fontFace)
return fontFace;
LOGFONT lf = QWindowsFontDatabase::fontDefToLOGFONT(fontDef);
// Get substitute name
static const char keyC[] = "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
const QString familyName = QString::fromWCharArray(lf.lfFaceName);
const QString nameSubstitute = QSettings(QLatin1String(keyC), QSettings::NativeFormat).value(familyName, familyName).toString();
memcpy(lf.lfFaceName, nameSubstitute.utf16(), sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE));
ComPtr<IDWriteFont> dwriteFont;
HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFromLOGFONT(&lf, &dwriteFont);
if (FAILED(hr)) {
qDebug("%s: CreateFontFromLOGFONT failed: %#x", __FUNCTION__, hr);
return fontFace;
}
hr = dwriteFont->CreateFontFace(&fontFace);
if (FAILED(hr)) {
qDebug("%s: CreateFontFace failed: %#x", __FUNCTION__, hr);
return fontFace;
}
if (fontFace)
fontCache.insert(fontDef, fontFace);
return fontFace;
}
};
QWindowsDirect2DPaintEngine::QWindowsDirect2DPaintEngine(QWindowsDirect2DBitmap *bitmap)
@ -1411,7 +1486,7 @@ void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticText
return;
}
ComPtr<IDWriteFontFace> fontFace = fontFaceFromFontEngine(staticTextItem->font, staticTextItem->fontEngine());
ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(staticTextItem->fontEngine());
if (!fontFace) {
qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__);
QPaintEngineEx::drawStaticTextItem(staticTextItem);
@ -1432,9 +1507,9 @@ void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticText
glyphOffsets[i].ascenderOffset = staticTextItem->glyphPositions[i].y.toReal() * -1;
}
drawGlyphRun(D2D1::Point2F(0, 0),
d->drawGlyphRun(D2D1::Point2F(0, 0),
fontFace.Get(),
staticTextItem->font,
staticTextItem->fontEngine()->fontDef,
staticTextItem->numGlyphs,
glyphIndices.constData(),
glyphAdvances.constData(),
@ -1459,7 +1534,7 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem
return;
}
ComPtr<IDWriteFontFace> fontFace = fontFaceFromFontEngine(*ti.f, ti.fontEngine);
ComPtr<IDWriteFontFace> fontFace = d->fontFaceFromFontEngine(ti.fontEngine);
if (!fontFace) {
qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__);
QPaintEngine::drawTextItem(p, textItem);
@ -1482,9 +1557,9 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem
const bool rtl = (ti.flags & QTextItem::RightToLeft);
const QPointF offset(rtl ? ti.width.toReal() : 0, 0);
drawGlyphRun(to_d2d_point_2f(p + offset),
d->drawGlyphRun(to_d2d_point_2f(p + offset),
fontFace.Get(),
ti.font(),
ti.fontEngine->fontDef,
ti.glyphs.numGlyphs,
glyphIndices.constData(),
glyphAdvances.constData(),
@ -1492,64 +1567,6 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem
rtl);
}
inline static FLOAT pointSizeToDIP(qreal pointSize, FLOAT dpiY)
{
return (pointSize + (pointSize / qreal(3.0))) * (dpiY / 96.0f);
}
inline static FLOAT pixelSizeToDIP(int pixelSize, FLOAT dpiY)
{
return FLOAT(pixelSize) * 96.0f / dpiY;
}
inline static FLOAT fontSizeInDIP(const QFont &font)
{
FLOAT dpiX, dpiY;
QWindowsDirect2DContext::instance()->d2dFactory()->GetDesktopDpi(&dpiX, &dpiY);
if (font.pixelSize() == -1) {
// font size was set as points
return pointSizeToDIP(font.pointSizeF(), dpiY);
} else {
// font size was set as pixels
return pixelSizeToDIP(font.pixelSize(), dpiY);
}
}
void QWindowsDirect2DPaintEngine::drawGlyphRun(const D2D1_POINT_2F &pos,
IDWriteFontFace *fontFace,
const QFont &font,
int numGlyphs,
const UINT16 *glyphIndices,
const FLOAT *glyphAdvances,
const DWRITE_GLYPH_OFFSET *glyphOffsets,
bool rtl)
{
Q_D(QWindowsDirect2DPaintEngine);
DWRITE_GLYPH_RUN glyphRun = {
fontFace, // IDWriteFontFace *fontFace;
fontSizeInDIP(font), // FLOAT fontEmSize;
numGlyphs, // UINT32 glyphCount;
glyphIndices, // const UINT16 *glyphIndices;
glyphAdvances, // const FLOAT *glyphAdvances;
glyphOffsets, // const DWRITE_GLYPH_OFFSET *glyphOffsets;
FALSE, // BOOL isSideways;
rtl ? 1 : 0 // UINT32 bidiLevel;
};
const bool antiAlias = bool((state()->renderHints & QPainter::TextAntialiasing)
&& !(font.styleStrategy() & QFont::NoAntialias));
d->dc()->SetTextAntialiasMode(antiAlias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
: D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
d->dc()->DrawGlyphRun(pos,
&glyphRun,
NULL,
d->pen.brush.Get(),
DWRITE_MEASURING_MODE_GDI_CLASSIC);
}
void QWindowsDirect2DPaintEngine::ensureBrush()
{
ensureBrush(state()->brush);
@ -1678,49 +1695,4 @@ void QWindowsDirect2DPaintEngine::adjustForAliasing(QPointF *point)
(*point) += adjustment;
}
Microsoft::WRL::ComPtr<IDWriteFontFace> QWindowsDirect2DPaintEngine::fontFaceFromFontEngine(const QFont &font, QFontEngine *fe)
{
Q_D(QWindowsDirect2DPaintEngine);
ComPtr<IDWriteFontFace> fontFace = d->fontCache.value(font);
if (fontFace)
return fontFace;
switch (fe->type()) {
case QFontEngine::Win:
{
QWindowsFontEngine *wfe = static_cast<QWindowsFontEngine *>(fe);
QSharedPointer<QWindowsFontEngineData> wfed = wfe->fontEngineData();
HGDIOBJ oldfont = wfe->selectDesignFont();
HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFaceFromHdc(wfed->hdc, &fontFace);
DeleteObject(SelectObject(wfed->hdc, oldfont));
if (FAILED(hr))
qWarning("%s: Could not create DirectWrite fontface from HDC: %#x", __FUNCTION__, hr);
}
break;
#ifndef QT_NO_DIRECTWRITE
case QFontEngine::DirectWrite:
{
QWindowsFontEngineDirectWrite *wfedw = static_cast<QWindowsFontEngineDirectWrite *>(fe);
fontFace = wfedw->directWriteFontFace();
}
break;
#endif // QT_NO_DIRECTWRITE
default:
qWarning("%s: Unknown font engine!", __FUNCTION__);
break;
}
if (fontFace)
d->fontCache.insert(font, fontFace);
return fontFace;
}
QT_END_NAMESPACE

View File

@ -105,10 +105,6 @@ public:
void drawTextItem(const QPointF &p, const QTextItem &textItem) Q_DECL_OVERRIDE;
private:
void drawGlyphRun(const D2D1_POINT_2F &pos, IDWriteFontFace *fontFace, const QFont &font,
int numGlyphs, const UINT16 *glyphIndices, const FLOAT *glyphAdvances,
const DWRITE_GLYPH_OFFSET *glyphOffsets, bool rtl);
void ensureBrush();
void ensureBrush(const QBrush &brush);
void ensurePen();
@ -122,8 +118,6 @@ private:
bool antiAliasingEnabled() const;
void adjustForAliasing(QRectF *rect);
void adjustForAliasing(QPointF *point);
Microsoft::WRL::ComPtr<IDWriteFontFace> fontFaceFromFontEngine(const QFont &font, QFontEngine *fe);
};
QT_END_NAMESPACE