From e5fcad302d86d316390c6b0f62759a067313e8a9 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Mon, 23 Mar 2009 10:18:55 +0100 Subject: Long live Qt 4.5! --- src/gui/text/qfontengine_win.cpp | 1575 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1575 insertions(+) create mode 100644 src/gui/text/qfontengine_win.cpp (limited to 'src/gui/text/qfontengine_win.cpp') diff --git a/src/gui/text/qfontengine_win.cpp b/src/gui/text/qfontengine_win.cpp new file mode 100644 index 0000000000..1996d44a4e --- /dev/null +++ b/src/gui/text/qfontengine_win.cpp @@ -0,0 +1,1575 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qfontengine_p.h" +#include "qtextengine_p.h" +#include +#include "qt_windows.h" +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include "qpaintengine.h" +#include "qvarlengtharray.h" +#include +#include + +#if defined(Q_OS_WINCE) +#include "qguifunctions_wince.h" +#endif + +//### mingw needed define +#ifndef TT_PRIM_CSPLINE +#define TT_PRIM_CSPLINE 3 +#endif + +#ifdef MAKE_TAG +#undef MAKE_TAG +#endif +// GetFontData expects the tags in little endian ;( +#define MAKE_TAG(ch1, ch2, ch3, ch4) (\ + (((quint32)(ch4)) << 24) | \ + (((quint32)(ch3)) << 16) | \ + (((quint32)(ch2)) << 8) | \ + ((quint32)(ch1)) \ + ) + +typedef BOOL (WINAPI *PtrGetCharWidthI)(HDC, UINT, UINT, LPWORD, LPINT); + +// common DC for all fonts + +QT_BEGIN_NAMESPACE + +class QtHDC +{ + HDC _hdc; +public: + QtHDC() + { + HDC displayDC = GetDC(0); + _hdc = CreateCompatibleDC(displayDC); + ReleaseDC(0, displayDC); + } + ~QtHDC() + { + if (_hdc) + DeleteDC(_hdc); + } + HDC hdc() const + { + return _hdc; + } +}; + +#ifndef QT_NO_THREAD +Q_GLOBAL_STATIC(QThreadStorage, local_shared_dc) +HDC shared_dc() +{ + QtHDC *&hdc = local_shared_dc()->localData(); + if (!hdc) + hdc = new QtHDC; + return hdc->hdc(); +} +#else +HDC shared_dc() +{ + return 0; +} +#endif + +static HFONT stock_sysfont = 0; + +static PtrGetCharWidthI ptrGetCharWidthI = 0; +static bool resolvedGetCharWidthI = false; + +static void resolveGetCharWidthI() +{ + if (resolvedGetCharWidthI) + return; + resolvedGetCharWidthI = true; + ptrGetCharWidthI = (PtrGetCharWidthI)QLibrary::resolve(QLatin1String("gdi32"), "GetCharWidthI"); +} + +// Copy a LOGFONTW struct into a LOGFONTA by converting the face name to an 8 bit value. +// This is needed when calling CreateFontIndirect on non-unicode windowses. +inline static void wa_copy_logfont(LOGFONTW *lfw, LOGFONTA *lfa) +{ + lfa->lfHeight = lfw->lfHeight; + lfa->lfWidth = lfw->lfWidth; + lfa->lfEscapement = lfw->lfEscapement; + lfa->lfOrientation = lfw->lfOrientation; + lfa->lfWeight = lfw->lfWeight; + lfa->lfItalic = lfw->lfItalic; + lfa->lfUnderline = lfw->lfUnderline; + lfa->lfCharSet = lfw->lfCharSet; + lfa->lfOutPrecision = lfw->lfOutPrecision; + lfa->lfClipPrecision = lfw->lfClipPrecision; + lfa->lfQuality = lfw->lfQuality; + lfa->lfPitchAndFamily = lfw->lfPitchAndFamily; + + QString fam = QString::fromUtf16((const ushort*)lfw->lfFaceName); + memcpy(lfa->lfFaceName, fam.toLocal8Bit().constData(), fam.length() + 1); +} + +// defined in qtextengine_win.cpp +typedef void *SCRIPT_CACHE; +typedef HRESULT (WINAPI *fScriptFreeCache)(SCRIPT_CACHE *); +extern fScriptFreeCache ScriptFreeCache; + +static inline quint32 getUInt(unsigned char *p) +{ + quint32 val; + val = *p++ << 24; + val |= *p++ << 16; + val |= *p++ << 8; + val |= *p; + + return val; +} + +static inline quint16 getUShort(unsigned char *p) +{ + quint16 val; + val = *p++ << 8; + val |= *p; + + return val; +} + +static inline HFONT systemFont() +{ + if (stock_sysfont == 0) + stock_sysfont = (HFONT)GetStockObject(SYSTEM_FONT); + return stock_sysfont; +} + + +// general font engine + +QFixed QFontEngineWin::lineThickness() const +{ + if(lineWidth > 0) + return lineWidth; + + return QFontEngine::lineThickness(); +} + +#if defined(Q_OS_WINCE) +static OUTLINETEXTMETRICW *getOutlineTextMetric(HDC hdc) +{ + int size; + size = GetOutlineTextMetricsW(hdc, 0, 0); + OUTLINETEXTMETRICW *otm = (OUTLINETEXTMETRICW *)malloc(size); + GetOutlineTextMetricsW(hdc, size, otm); + return otm; +} +#else +static OUTLINETEXTMETRICA *getOutlineTextMetric(HDC hdc) +{ + int size; + size = GetOutlineTextMetricsA(hdc, 0, 0); + OUTLINETEXTMETRICA *otm = (OUTLINETEXTMETRICA *)malloc(size); + GetOutlineTextMetricsA(hdc, size, otm); + return otm; +} +#endif + +void QFontEngineWin::getCMap() +{ + QT_WA({ + ttf = (bool)(tm.w.tmPitchAndFamily & TMPF_TRUETYPE); + } , { + ttf = (bool)(tm.a.tmPitchAndFamily & TMPF_TRUETYPE); + }); + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + bool symb = false; + if (ttf) { + cmapTable = getSfntTable(qbswap(MAKE_TAG('c', 'm', 'a', 'p'))); + int size = 0; + cmap = QFontEngine::getCMap(reinterpret_cast(cmapTable.constData()), + cmapTable.size(), &symb, &size); + } + if (!cmap) { + ttf = false; + symb = false; + } + symbol = symb; + designToDevice = 1; + _faceId.index = 0; + if(cmap) { +#if defined(Q_OS_WINCE) + OUTLINETEXTMETRICW *otm = getOutlineTextMetric(hdc); +#else + OUTLINETEXTMETRICA *otm = getOutlineTextMetric(hdc); +#endif + designToDevice = QFixed((int)otm->otmEMSquare)/int(otm->otmTextMetrics.tmHeight); + unitsPerEm = otm->otmEMSquare; + x_height = (int)otm->otmsXHeight; + loadKerningPairs(designToDevice); + _faceId.filename = (char *)otm + (int)otm->otmpFullName; + lineWidth = otm->otmsUnderscoreSize; + fsType = otm->otmfsType; + free(otm); + } else { + unitsPerEm = tm.w.tmHeight; + } +} + + +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; +} + +int QFontEngineWin::getGlyphIndexes(const QChar *str, int numChars, QGlyphLayout *glyphs, bool mirrored) const +{ + int i = 0; + int glyph_pos = 0; + if (mirrored) { +#if defined(Q_OS_WINCE) + { +#else + if (symbol) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); + if (!glyphs->glyphs[glyph_pos] && uc < 0x100) + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000); + } + } else if (ttf) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, QChar::mirroredChar(uc)); + } + } else { +#endif + ushort first, last; + QT_WA({ + first = tm.w.tmFirstChar; + last = tm.w.tmLastChar; + }, { + first = tm.a.tmFirstChar; + last = tm.a.tmLastChar; + }); + for (; i < numChars; ++i, ++glyph_pos) { + uint ucs = QChar::mirroredChar(getChar(str, i, numChars)); + if ( +#ifdef Q_OS_WINCE + tm.w.tmFirstChar > 60000 || // see line 375 +#endif + ucs >= first && ucs <= last) + glyphs->glyphs[glyph_pos] = ucs; + else + glyphs->glyphs[glyph_pos] = 0; + } + } + } else { +#if defined(Q_OS_WINCE) + { +#else + if (symbol) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[i] = getTrueTypeGlyphIndex(cmap, uc); + if(!glyphs->glyphs[glyph_pos] && uc < 0x100) + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc + 0xf000); + } + } else if (ttf) { + for (; i < numChars; ++i, ++glyph_pos) { + unsigned int uc = getChar(str, i, numChars); + glyphs->glyphs[glyph_pos] = getTrueTypeGlyphIndex(cmap, uc); + } + } else { +#endif + ushort first, last; + QT_WA({ + first = tm.w.tmFirstChar; + last = tm.w.tmLastChar; + }, { + first = tm.a.tmFirstChar; + last = tm.a.tmLastChar; + }); + for (; i < numChars; ++i, ++glyph_pos) { + uint uc = getChar(str, i, numChars); + if ( +#ifdef Q_OS_WINCE + tm.w.tmFirstChar > 60000 || // see comment in QFontEngineWin +#endif + uc >= first && uc <= last) + glyphs->glyphs[glyph_pos] = uc; + else + glyphs->glyphs[glyph_pos] = 0; + } + } + } + glyphs->numGlyphs = glyph_pos; + return glyph_pos; +} + + +QFontEngineWin::QFontEngineWin(const QString &name, HFONT _hfont, bool stockFont, LOGFONT lf) +{ + //qDebug("regular windows font engine created: font='%s', size=%d", name, lf.lfHeight); + + _name = name; + + cmap = 0; + hfont = _hfont; + logfont = lf; + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + this->stockFont = stockFont; + fontDef.pixelSize = -lf.lfHeight; + + lbearing = SHRT_MIN; + rbearing = SHRT_MIN; + synthesized_flags = -1; + lineWidth = -1; + x_height = -1; + + BOOL res; + QT_WA({ + res = GetTextMetricsW(hdc, &tm.w); + } , { + res = GetTextMetricsA(hdc, &tm.a); + }); + fontDef.fixedPitch = !(tm.w.tmPitchAndFamily & TMPF_FIXED_PITCH); + if (!res) + qErrnoWarning("QFontEngineWin: GetTextMetrics failed"); + + cache_cost = tm.w.tmHeight * tm.w.tmAveCharWidth * 2000; + getCMap(); + + useTextOutA = false; +#ifndef Q_OS_WINCE + // TextOutW doesn't work for symbol fonts on Windows 95! + // since we're using glyph indices we don't care for ttfs about this! + if (QSysInfo::WindowsVersion == QSysInfo::WV_95 && !ttf && + (_name == QLatin1String("Marlett") || _name == QLatin1String("Symbol") || + _name == QLatin1String("Webdings") || _name == QLatin1String("Wingdings"))) + useTextOutA = true; +#endif + widthCache = 0; + widthCacheSize = 0; + designAdvances = 0; + designAdvancesSize = 0; + + if (!resolvedGetCharWidthI) + resolveGetCharWidthI(); +} + +QFontEngineWin::~QFontEngineWin() +{ + if (designAdvances) + free(designAdvances); + + if (widthCache) + free(widthCache); + + // make sure we aren't by accident still selected + SelectObject(shared_dc(), systemFont()); + + if (!stockFont) { + if (!DeleteObject(hfont)) + qErrnoWarning("QFontEngineWin: failed to delete non-stock font..."); + } +} + +HGDIOBJ QFontEngineWin::selectDesignFont(QFixed *overhang) const +{ + LOGFONT f = logfont; + f.lfHeight = unitsPerEm; + HFONT designFont; + QT_WA({ + designFont = CreateFontIndirectW(&f); + }, { + LOGFONTA fa; + wa_copy_logfont(&f, &fa); + designFont = CreateFontIndirectA(&fa); + }); + HGDIOBJ oldFont = SelectObject(shared_dc(), designFont); + + if (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) { + BOOL res; + QT_WA({ + TEXTMETRICW tm; + res = GetTextMetricsW(shared_dc(), &tm); + if (!res) + qErrnoWarning("QFontEngineWin: GetTextMetrics failed"); + *overhang = QFixed((int)tm.tmOverhang) / designToDevice; + } , { + TEXTMETRICA tm; + res = GetTextMetricsA(shared_dc(), &tm); + if (!res) + qErrnoWarning("QFontEngineWin: GetTextMetrics failed"); + *overhang = QFixed((int)tm.tmOverhang) / designToDevice; + }); + } else { + *overhang = 0; + } + return oldFont; +} + +bool QFontEngineWin::stringToCMap(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags) const +{ + if (*nglyphs < len) { + *nglyphs = len; + return false; + } + + *nglyphs = getGlyphIndexes(str, len, glyphs, flags & QTextEngine::RightToLeft); + + if (flags & QTextEngine::GlyphIndicesOnly) + return true; + +#if defined(Q_OS_WINCE) + HDC hdc = shared_dc(); + if (flags & QTextEngine::DesignMetrics) { + HGDIOBJ oldFont = 0; + QFixed overhang = 0; + + int glyph_pos = 0; + for(register int i = 0; i < len; i++) { + bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1 + && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); + unsigned int glyph = glyphs->glyphs[glyph_pos]; + if(int(glyph) >= designAdvancesSize) { + int newSize = (glyph + 256) >> 8 << 8; + designAdvances = (QFixed *)realloc(designAdvances, newSize*sizeof(QFixed)); + for(int i = designAdvancesSize; i < newSize; ++i) + designAdvances[i] = -1000000; + designAdvancesSize = newSize; + } + if(designAdvances[glyph] < -999999) { + if(!oldFont) + oldFont = selectDesignFont(&overhang); + SIZE size = {0, 0}; + GetTextExtentPoint32W(hdc, (wchar_t *)(str+i), surrogate ? 2 : 1, &size); + designAdvances[glyph] = QFixed((int)size.cx)/designToDevice; + } + glyphs->advances_x[glyph_pos] = designAdvances[glyph]; + glyphs->advances_y[glyph_pos] = 0; + if (surrogate) + ++i; + ++glyph_pos; + } + if(oldFont) + DeleteObject(SelectObject(hdc, oldFont)); + } else { + int glyph_pos = 0; + HGDIOBJ oldFont = 0; + + for(register int i = 0; i < len; i++) { + bool surrogate = (str[i].unicode() >= 0xd800 && str[i].unicode() < 0xdc00 && i < len-1 + && str[i+1].unicode() >= 0xdc00 && str[i+1].unicode() < 0xe000); + unsigned int glyph = glyphs->glyphs[glyph_pos]; + + glyphs->advances_y[glyph_pos] = 0; + + if (glyph >= widthCacheSize) { + int newSize = (glyph + 256) >> 8 << 8; + widthCache = (unsigned char *)realloc(widthCache, newSize*sizeof(QFixed)); + memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); + widthCacheSize = newSize; + } + glyphs->advances_x[glyph_pos] = widthCache[glyph]; + // font-width cache failed + if (glyphs->advances_x[glyph_pos] == 0) { + SIZE size = {0, 0}; + if (!oldFont) + oldFont = SelectObject(hdc, hfont); + GetTextExtentPoint32W(hdc, (wchar_t *)str + i, surrogate ? 2 : 1, &size); + glyphs->advances_x[glyph_pos] = size.cx; + // if glyph's within cache range, store it for later + if (size.cx > 0 && size.cx < 0x100) + widthCache[glyph] = size.cx; + } + + if (surrogate) + ++i; + ++glyph_pos; + } + + if (oldFont) + SelectObject(hdc, oldFont); + } +#else + recalcAdvances(glyphs, flags); +#endif + return true; +} + +void QFontEngineWin::recalcAdvances(QGlyphLayout *glyphs, QTextEngine::ShaperFlags flags) const +{ + HGDIOBJ oldFont = 0; + HDC hdc = shared_dc(); + if (ttf && (flags & QTextEngine::DesignMetrics)) { + QFixed overhang = 0; + + for(int i = 0; i < glyphs->numGlyphs; i++) { + unsigned int glyph = glyphs->glyphs[i]; + if(int(glyph) >= designAdvancesSize) { + int newSize = (glyph + 256) >> 8 << 8; + designAdvances = (QFixed *)realloc(designAdvances, newSize*sizeof(QFixed)); + for(int i = designAdvancesSize; i < newSize; ++i) + designAdvances[i] = -1000000; + designAdvancesSize = newSize; + } + if(designAdvances[glyph] < -999999) { + if(!oldFont) + oldFont = selectDesignFont(&overhang); + + if (ptrGetCharWidthI) { + int width = 0; + ptrGetCharWidthI(hdc, glyph, 1, 0, &width); + + designAdvances[glyph] = QFixed(width) / designToDevice; + } else { +#ifndef Q_OS_WINCE + GLYPHMETRICS gm; + DWORD res = GDI_ERROR; + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + QT_WA({ + res = GetGlyphOutlineW(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX|GGO_NATIVE, &gm, 0, 0, &mat); + } , { + res = GetGlyphOutlineA(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX|GGO_NATIVE, &gm, 0, 0, &mat); + }); + + if (res != GDI_ERROR) { + designAdvances[glyph] = QFixed(gm.gmCellIncX) / designToDevice; + } +#endif + } + } + glyphs->advances_x[i] = designAdvances[glyph]; + glyphs->advances_y[i] = 0; + } + if(oldFont) + DeleteObject(SelectObject(hdc, oldFont)); + } else { + int overhang = (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) ? tm.a.tmOverhang : 0; + + for(int i = 0; i < glyphs->numGlyphs; i++) { + unsigned int glyph = glyphs->glyphs[i]; + + glyphs->advances_y[i] = 0; + + if (glyph >= widthCacheSize) { + int newSize = (glyph + 256) >> 8 << 8; + widthCache = (unsigned char *)realloc(widthCache, newSize*sizeof(QFixed)); + memset(widthCache + widthCacheSize, 0, newSize - widthCacheSize); + widthCacheSize = newSize; + } + glyphs->advances_x[i] = widthCache[glyph]; + // font-width cache failed + if (glyphs->advances_x[i] == 0) { + int width = 0; + if (!oldFont) + oldFont = SelectObject(hdc, hfont); + + if (!ttf) { + QChar ch[2] = { ushort(glyph), 0 }; + int chrLen = 1; + if (glyph > 0xffff) { + ch[0] = QChar::highSurrogate(glyph); + ch[1] = QChar::lowSurrogate(glyph); + ++chrLen; + } + SIZE size = {0, 0}; + GetTextExtentPoint32W(hdc, (wchar_t *)ch, chrLen, &size); + width = size.cx; + } else if (ptrGetCharWidthI) { + ptrGetCharWidthI(hdc, glyph, 1, 0, &width); + + width -= overhang; + } else { +#ifndef Q_OS_WINCE + GLYPHMETRICS gm; + DWORD res = GDI_ERROR; + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + QT_WA({ + res = GetGlyphOutlineW(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat); + } , { + res = GetGlyphOutlineA(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat); + }); + + if (res != GDI_ERROR) { + width = gm.gmCellIncX; + } +#endif + } + glyphs->advances_x[i] = width; + // if glyph's within cache range, store it for later + if (width > 0 && width < 0x100) + widthCache[glyph] = width; + } + } + + if (oldFont) + SelectObject(hdc, oldFont); + } +} + +glyph_metrics_t QFontEngineWin::boundingBox(const QGlyphLayout &glyphs) +{ + if (glyphs.numGlyphs == 0) + return glyph_metrics_t(); + + QFixed w = 0; + for (int i = 0; i < glyphs.numGlyphs; ++i) + w += glyphs.effectiveAdvance(i); + + return glyph_metrics_t(0, -tm.w.tmAscent, w, tm.w.tmHeight, w, 0); +} + + + + +#ifndef Q_OS_WINCE +typedef HRESULT (WINAPI *pGetCharABCWidthsFloat)(HDC, UINT, UINT, LPABCFLOAT); +static pGetCharABCWidthsFloat qt_GetCharABCWidthsFloat = 0; +#endif + +glyph_metrics_t QFontEngineWin::boundingBox(glyph_t glyph, const QTransform &t) +{ +#ifndef Q_OS_WINCE + GLYPHMETRICS gm; + + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + if(!ttf) { + SIZE s = {0, 0}; + WCHAR ch = glyph; + int width; + int overhang = 0; + static bool resolved = false; + if (!resolved) { + QLibrary lib(QLatin1String("gdi32")); + qt_GetCharABCWidthsFloat = (pGetCharABCWidthsFloat) lib.resolve("GetCharABCWidthsFloatW"); + resolved = true; + } + if (QT_WA_INLINE(true, false) && qt_GetCharABCWidthsFloat) { + ABCFLOAT abc; + qt_GetCharABCWidthsFloat(hdc, ch, ch, &abc); + width = qRound(abc.abcfB); + } else { + GetTextExtentPoint32W(hdc, &ch, 1, &s); + overhang = (QSysInfo::WindowsVersion & QSysInfo::WV_DOS_based) ? tm.a.tmOverhang : 0; + width = s.cx; + } + + return glyph_metrics_t(0, -tm.a.tmAscent, + width, tm.a.tmHeight, + width-overhang, 0).transformed(t); + } else { + DWORD res = 0; + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + + if (t.type() > QTransform::TxTranslate) { + // We need to set the transform using the HDC's world + // matrix rather than using the MAT2 above, because the + // results provided when transforming via MAT2 does not + // match the glyphs that are drawn using a WorldTransform + XFORM xform; + xform.eM11 = t.m11(); + xform.eM12 = t.m12(); + xform.eM21 = t.m21(); + xform.eM22 = t.m22(); + xform.eDx = 0; + xform.eDy = 0; + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + } + + QT_WA({ + res = GetGlyphOutlineW(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat); + } , { + res = GetGlyphOutlineA(hdc, glyph, GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, 0, &mat); + }); + if (t.type() > QTransform::TxTranslate) { + XFORM xform; + xform.eM11 = xform.eM22 = 1; + xform.eM12 = xform.eM21 = xform.eDx = xform.eDy = 0; + SetWorldTransform(hdc, &xform); + SetGraphicsMode(hdc, GM_COMPATIBLE); + } + + if (res != GDI_ERROR) { + return glyph_metrics_t(gm.gmptGlyphOrigin.x, -gm.gmptGlyphOrigin.y, + (int)gm.gmBlackBoxX, (int)gm.gmBlackBoxY, gm.gmCellIncX, gm.gmCellIncY); + } + } + return glyph_metrics_t(); +#else + HDC hdc = shared_dc(); + HGDIOBJ oldFont = SelectObject(hdc, hfont); + + ABC abc; + int width; + int advance; +#ifdef GWES_MGTT // true type fonts + if (GetCharABCWidths(hdc, glyph, glyph, &abc)) { + width = qAbs(abc.abcA) + abc.abcB + qAbs(abc.abcC); + advance = abc.abcA + abc.abcB + abc.abcC; + } + else +#endif +#if defined(GWES_MGRAST) || defined(GWES_MGRAST2) // raster fonts + if (GetCharWidth32(hdc, glyph, glyph, &width)) { + advance = width; + } + else +#endif + { // fallback + width = tm.w.tmMaxCharWidth; + advance = width; + } + + SelectObject(hdc, oldFont); + return glyph_metrics_t(0, -tm.w.tmAscent, width, tm.w.tmHeight, advance, 0).transformed(t); +#endif +} + +QFixed QFontEngineWin::ascent() const +{ + return tm.w.tmAscent; +} + +QFixed QFontEngineWin::descent() const +{ + return tm.w.tmDescent; +} + +QFixed QFontEngineWin::leading() const +{ + return tm.w.tmExternalLeading; +} + + +QFixed QFontEngineWin::xHeight() const +{ + if(x_height >= 0) + return x_height; + return QFontEngine::xHeight(); +} + +QFixed QFontEngineWin::averageCharWidth() const +{ + return tm.w.tmAveCharWidth; +} + +qreal QFontEngineWin::maxCharWidth() const +{ + return tm.w.tmMaxCharWidth; +} + +enum { max_font_count = 256 }; +static const ushort char_table[] = { + 40, + 67, + 70, + 75, + 86, + 88, + 89, + 91, + 102, + 114, + 124, + 127, + 205, + 645, + 884, + 922, + 1070, + 12386, + 0 +}; + +static const int char_table_entries = sizeof(char_table)/sizeof(ushort); + + +qreal QFontEngineWin::minLeftBearing() const +{ + if (lbearing == SHRT_MIN) + minRightBearing(); // calculates both + + return lbearing; +} + +qreal QFontEngineWin::minRightBearing() const +{ +#ifdef Q_OS_WINCE + if (rbearing == SHRT_MIN) { + int ml = 0; + int mr = 0; + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + if (ttf) { + ABC *abc = 0; + int n = QT_WA_INLINE(tm.w.tmLastChar - tm.w.tmFirstChar, tm.a.tmLastChar - tm.a.tmFirstChar); + if (n <= max_font_count) { + abc = new ABC[n+1]; + GetCharABCWidths(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc); + } else { + abc = new ABC[char_table_entries+1]; + for(int i = 0; i < char_table_entries; i++) + GetCharABCWidths(hdc, char_table[i], char_table[i], abc+i); + n = char_table_entries; + } + ml = abc[0].abcA; + mr = abc[0].abcC; + for (int i = 1; i < n; i++) { + if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) { + ml = qMin(ml,abc[i].abcA); + mr = qMin(mr,abc[i].abcC); + } + } + delete [] abc; + } else { + ml = 0; + mr = -tm.a.tmOverhang; + } + lbearing = ml; + rbearing = mr; + } + + return rbearing; +#else + if (rbearing == SHRT_MIN) { + int ml = 0; + int mr = 0; + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + if (ttf) { + ABC *abc = 0; + int n = QT_WA_INLINE(tm.w.tmLastChar - tm.w.tmFirstChar, tm.a.tmLastChar - tm.a.tmFirstChar); + if (n <= max_font_count) { + abc = new ABC[n+1]; + QT_WA({ + GetCharABCWidths(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc); + }, { + GetCharABCWidthsA(hdc,tm.a.tmFirstChar,tm.a.tmLastChar,abc); + }); + } else { + abc = new ABC[char_table_entries+1]; + QT_WA({ + for(int i = 0; i < char_table_entries; i++) + GetCharABCWidths(hdc, char_table[i], char_table[i], abc+i); + }, { + for(int i = 0; i < char_table_entries; i++) { + QByteArray w = QString(QChar(char_table[i])).toLocal8Bit(); + if (w.length() == 1) { + uint ch8 = (uchar)w[0]; + GetCharABCWidthsA(hdc, ch8, ch8, abc+i); + } + } + }); + n = char_table_entries; + } + ml = abc[0].abcA; + mr = abc[0].abcC; + for (int i = 1; i < n; i++) { + if (abc[i].abcA + abc[i].abcB + abc[i].abcC != 0) { + ml = qMin(ml,abc[i].abcA); + mr = qMin(mr,abc[i].abcC); + } + } + delete [] abc; + } else { + QT_WA({ + ABCFLOAT *abc = 0; + int n = tm.w.tmLastChar - tm.w.tmFirstChar+1; + if (n <= max_font_count) { + abc = new ABCFLOAT[n]; + GetCharABCWidthsFloat(hdc, tm.w.tmFirstChar, tm.w.tmLastChar, abc); + } else { + abc = new ABCFLOAT[char_table_entries]; + for(int i = 0; i < char_table_entries; i++) + GetCharABCWidthsFloat(hdc, char_table[i], char_table[i], abc+i); + n = char_table_entries; + } + float fml = abc[0].abcfA; + float fmr = abc[0].abcfC; + for (int i=1; i string->unicode() || tm.w.tmLastChar < string->unicode()) + return false; + } + }, { + while(len--) { + if (tm.a.tmFirstChar > string->unicode() || tm.a.tmLastChar < string->unicode()) + return false; + } + }); + } + return true; +} + +QFontEngine::Type QFontEngineWin::type() const +{ + return QFontEngine::Win; +} + +static inline double qt_fixed_to_double(const FIXED &p) { + return ((p.value << 16) + p.fract) / 65536.0; +} + +static inline QPointF qt_to_qpointf(const POINTFX &pt, qreal scale) { + return QPointF(qt_fixed_to_double(pt.x) * scale, -qt_fixed_to_double(pt.y) * scale); +} + +#ifndef GGO_UNHINTED +#define GGO_UNHINTED 0x0100 +#endif + +static bool addGlyphToPath(glyph_t glyph, const QFixedPoint &position, HDC hdc, + QPainterPath *path, bool ttf, glyph_metrics_t *metric = 0, qreal scale = 1) +{ +#if defined(Q_OS_WINCE) + Q_UNUSED(glyph); + Q_UNUSED(hdc); +#endif + MAT2 mat; + mat.eM11.value = mat.eM22.value = 1; + mat.eM11.fract = mat.eM22.fract = 0; + mat.eM21.value = mat.eM12.value = 0; + mat.eM21.fract = mat.eM12.fract = 0; + uint glyphFormat = GGO_NATIVE; + + if (ttf) + glyphFormat |= GGO_GLYPH_INDEX; + + GLYPHMETRICS gMetric; + memset(&gMetric, 0, sizeof(GLYPHMETRICS)); + int bufferSize = GDI_ERROR; +#if !defined(Q_OS_WINCE) + QT_WA( { + bufferSize = GetGlyphOutlineW(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); + }, { + bufferSize = GetGlyphOutlineA(hdc, glyph, glyphFormat, &gMetric, 0, 0, &mat); + }); +#endif + if ((DWORD)bufferSize == GDI_ERROR) { + return false; + } + + void *dataBuffer = new char[bufferSize]; + DWORD ret = GDI_ERROR; +#if !defined(Q_OS_WINCE) + QT_WA( { + ret = GetGlyphOutlineW(hdc, glyph, glyphFormat, &gMetric, bufferSize, + dataBuffer, &mat); + }, { + ret = GetGlyphOutlineA(hdc, glyph, glyphFormat, &gMetric, bufferSize, + dataBuffer, &mat); + } ); +#endif + if (ret == GDI_ERROR) { + delete [](char *)dataBuffer; + return false; + } + + if(metric) { + // #### obey scale + *metric = glyph_metrics_t(gMetric.gmptGlyphOrigin.x, -gMetric.gmptGlyphOrigin.y, + (int)gMetric.gmBlackBoxX, (int)gMetric.gmBlackBoxY, + gMetric.gmCellIncX, gMetric.gmCellIncY); + } + + int offset = 0; + int headerOffset = 0; + TTPOLYGONHEADER *ttph = 0; + + QPointF oset = position.toPointF(); + while (headerOffset < bufferSize) { + ttph = (TTPOLYGONHEADER*)((char *)dataBuffer + headerOffset); + + QPointF lastPoint(qt_to_qpointf(ttph->pfxStart, scale)); + path->moveTo(lastPoint + oset); + offset += sizeof(TTPOLYGONHEADER); + TTPOLYCURVE *curve; + while (offsetcb)) { + curve = (TTPOLYCURVE*)((char*)(dataBuffer) + offset); + switch (curve->wType) { + case TT_PRIM_LINE: { + for (int i=0; icpfx; ++i) { + QPointF p = qt_to_qpointf(curve->apfx[i], scale) + oset; + path->lineTo(p); + } + break; + } + case TT_PRIM_QSPLINE: { + const QPainterPath::Element &elm = path->elementAt(path->elementCount()-1); + QPointF prev(elm.x, elm.y); + QPointF endPoint; + for (int i=0; icpfx - 1; ++i) { + QPointF p1 = qt_to_qpointf(curve->apfx[i], scale) + oset; + QPointF p2 = qt_to_qpointf(curve->apfx[i+1], scale) + oset; + if (i < curve->cpfx - 2) { + endPoint = QPointF((p1.x() + p2.x()) / 2, (p1.y() + p2.y()) / 2); + } else { + endPoint = p2; + } + + path->quadTo(p1, endPoint); + prev = endPoint; + } + + break; + } + case TT_PRIM_CSPLINE: { + for (int i=0; icpfx; ) { + QPointF p2 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + QPointF p3 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + QPointF p4 = qt_to_qpointf(curve->apfx[i++], scale) + oset; + path->cubicTo(p2, p3, p4); + } + break; + } + default: + qWarning("QFontEngineWin::addOutlineToPath, unhandled switch case"); + } + offset += sizeof(TTPOLYCURVE) + (curve->cpfx-1) * sizeof(POINTFX); + } + path->closeSubpath(); + headerOffset += ttph->cb; + } + delete [] (char*)dataBuffer; + + return true; +} + +void QFontEngineWin::addGlyphsToPath(glyph_t *glyphs, QFixedPoint *positions, int nglyphs, + QPainterPath *path, QTextItem::RenderFlags) +{ + LOGFONT lf = logfont; + // The sign must be negative here to make sure we match against character height instead of + // hinted cell height. This ensures that we get linear matching, and we need this for + // paths since we later on apply a scaling transform to the glyph outline to get the + // font at the correct pixel size. + lf.lfHeight = -unitsPerEm; + lf.lfWidth = 0; + HFONT hf; + QT_WA({ + hf = CreateFontIndirectW(&lf); + }, { + LOGFONTA lfa; + wa_copy_logfont(&lf, &lfa); + hf = CreateFontIndirectA(&lfa); + }); + HDC hdc = shared_dc(); + HGDIOBJ oldfont = SelectObject(hdc, hf); + + for(int i = 0; i < nglyphs; ++i) { + if (!addGlyphToPath(glyphs[i], positions[i], hdc, path, ttf, /*metric*/0, + qreal(fontDef.pixelSize) / unitsPerEm)) { + // Some windows fonts, like "Modern", are vector stroke + // fonts, which are reported as TMPF_VECTOR but do not + // support GetGlyphOutline, and thus we set this bit so + // that addOutLineToPath can check it and return safely... + hasOutline = false; + break; + } + } + DeleteObject(SelectObject(hdc, oldfont)); +} + +void QFontEngineWin::addOutlineToPath(qreal x, qreal y, const QGlyphLayout &glyphs, + QPainterPath *path, QTextItem::RenderFlags flags) +{ +#if !defined(Q_OS_WINCE) + if(tm.w.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) { + hasOutline = true; + QFontEngine::addOutlineToPath(x, y, glyphs, path, flags); + if (hasOutline) { + // has_outline is set to false if addGlyphToPath gets + // false from GetGlyphOutline, meaning its not an outline + // font. + return; + } + } +#endif + QFontEngine::addBitmapFontToPath(x, y, glyphs, path, flags); +} + +QFontEngine::FaceId QFontEngineWin::faceId() const +{ + return _faceId; +} + +QT_BEGIN_INCLUDE_NAMESPACE +#include +QT_END_INCLUDE_NAMESPACE + +int QFontEngineWin::synthesized() const +{ + if(synthesized_flags == -1) { + synthesized_flags = 0; + if(ttf) { + const DWORD HEAD = MAKE_TAG('h', 'e', 'a', 'd'); + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + uchar data[4]; + GetFontData(hdc, HEAD, 44, &data, 4); + USHORT macStyle = getUShort(data); + if (tm.w.tmItalic && !(macStyle & 2)) + synthesized_flags = SynthesizedItalic; + if (fontDef.stretch != 100 && ttf) + synthesized_flags |= SynthesizedStretch; + if (tm.w.tmWeight >= 500 && !(macStyle & 1)) + synthesized_flags |= SynthesizedBold; + //qDebug() << "font is" << _name << + // "it=" << (macStyle & 2) << fontDef.style << "flags=" << synthesized_flags; + } + } + return synthesized_flags; +} + +QFixed QFontEngineWin::emSquareSize() const +{ + return unitsPerEm; +} + +QFontEngine::Properties QFontEngineWin::properties() const +{ + + LOGFONT lf = logfont; + lf.lfHeight = unitsPerEm; + HFONT hf; + QT_WA({ + hf = CreateFontIndirectW(&lf); + }, { + LOGFONTA lfa; + wa_copy_logfont(&lf, &lfa); + hf = CreateFontIndirectA(&lfa); + }); + HDC hdc = shared_dc(); + HGDIOBJ oldfont = SelectObject(hdc, hf); +#if defined(Q_OS_WINCE) + OUTLINETEXTMETRICW *otm = getOutlineTextMetric(hdc); +#else + OUTLINETEXTMETRICA *otm = getOutlineTextMetric(hdc); +#endif + Properties p; + p.emSquare = unitsPerEm; + p.italicAngle = otm->otmItalicAngle; + p.postscriptName = (char *)otm + (int)otm->otmpFamilyName; + p.postscriptName += (char *)otm + (int)otm->otmpStyleName; +#ifndef QT_NO_PRINTER + p.postscriptName = QPdf::stripSpecialCharacters(p.postscriptName); +#endif + p.boundingBox = QRectF(otm->otmrcFontBox.left, -otm->otmrcFontBox.top, + otm->otmrcFontBox.right - otm->otmrcFontBox.left, + otm->otmrcFontBox.top - otm->otmrcFontBox.bottom); + p.ascent = otm->otmAscent; + p.descent = -otm->otmDescent; + p.leading = (int)otm->otmLineGap; + p.capHeight = 0; + p.lineWidth = otm->otmsUnderscoreSize; + free(otm); + DeleteObject(SelectObject(hdc, oldfont)); + return p; +} + +void QFontEngineWin::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metrics_t *metrics) +{ + LOGFONT lf = logfont; + lf.lfHeight = unitsPerEm; + int flags = synthesized(); + if(flags & SynthesizedItalic) + lf.lfItalic = false; + lf.lfWidth = 0; + HFONT hf; + QT_WA({ + hf = CreateFontIndirectW(&lf); + }, { + LOGFONTA lfa; + wa_copy_logfont(&lf, &lfa); + hf = CreateFontIndirectA(&lfa); + }); + HDC hdc = shared_dc(); + HGDIOBJ oldfont = SelectObject(hdc, hf); + QFixedPoint p; + p.x = 0; + p.y = 0; + addGlyphToPath(glyph, p, hdc, path, ttf, metrics); + DeleteObject(SelectObject(hdc, oldfont)); +} + +bool QFontEngineWin::getSfntTableData(uint tag, uchar *buffer, uint *length) const +{ + if (!ttf) + return false; + HDC hdc = shared_dc(); + SelectObject(hdc, hfont); + DWORD t = qbswap(tag); + *length = GetFontData(hdc, t, 0, buffer, *length); + return *length != GDI_ERROR; +} + +#if !defined(CLEARTYPE_QUALITY) +# define CLEARTYPE_QUALITY 5 +#endif + + +QNativeImage *QFontEngineWin::drawGDIGlyph(HFONT font, glyph_t glyph, int margin, + const QTransform &t) +{ + glyph_metrics_t gm = boundingBox(glyph); + +// printf(" -> for glyph %4x\n", glyph); + + int gx = gm.x.toInt(); + int gy = gm.y.toInt(); + int iw = gm.width.toInt(); + int ih = gm.height.toInt(); + + if (iw <= 0 || iw <= 0) + return 0; + + bool has_transformation = t.type() > QTransform::TxTranslate; + +#ifndef Q_OS_WINCE + unsigned int options = ttf ? ETO_GLYPH_INDEX : 0; + XFORM xform; + + if (has_transformation) { + xform.eM11 = t.m11(); + xform.eM12 = t.m12(); + xform.eM21 = t.m21(); + xform.eM22 = t.m22(); + xform.eDx = margin; + xform.eDy = margin; + + QtHDC qthdc; + HDC hdc = qthdc.hdc(); + + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + HGDIOBJ old_font = SelectObject(hdc, font); + + int ggo_options = GGO_METRICS | (ttf ? GGO_GLYPH_INDEX : 0); + GLYPHMETRICS tgm; + MAT2 mat; + memset(&mat, 0, sizeof(mat)); + mat.eM11.value = mat.eM22.value = 1; + + int error = 0; + QT_WA( { + error = GetGlyphOutlineW(hdc, glyph, ggo_options, &tgm, 0, 0, &mat); + }, { + error = GetGlyphOutlineA(hdc, glyph, ggo_options, &tgm, 0, 0, &mat); + } ); + + if (error == GDI_ERROR) { + qWarning("QWinFontEngine: unable to query transformed glyph metrics..."); + return 0; + } + + iw = tgm.gmBlackBoxX; + ih = tgm.gmBlackBoxY; + + xform.eDx -= tgm.gmptGlyphOrigin.x; + xform.eDy += tgm.gmptGlyphOrigin.y; + + SetGraphicsMode(hdc, GM_COMPATIBLE); + SelectObject(hdc, old_font); + } +#else // else winc + unsigned int options = 0; +#ifdef DEBUG + Q_ASSERT(!has_transformation); +#else + Q_UNUSED(has_transformation); +#endif +#endif + + QNativeImage *ni = new QNativeImage(iw + 2 * margin, + ih + 2 * margin, + QNativeImage::systemFormat(), true); + ni->image.fill(0xffffffff); + + HDC hdc = ni->hdc; + + SelectObject(hdc, GetStockObject(NULL_BRUSH)); + SelectObject(hdc, GetStockObject(BLACK_PEN)); + SetTextColor(hdc, RGB(0,0,0)); + SetBkMode(hdc, TRANSPARENT); + SetTextAlign(hdc, TA_BASELINE); + + HGDIOBJ old_font = SelectObject(hdc, font); + +#ifndef Q_OS_WINCE + if (has_transformation) { + SetGraphicsMode(hdc, GM_ADVANCED); + SetWorldTransform(hdc, &xform); + ExtTextOutW(hdc, 0, 0, options, 0, (LPCWSTR) &glyph, 1, 0); + } else +#endif + { + ExtTextOutW(hdc, -gx + margin, -gy + margin, options, 0, (LPCWSTR) &glyph, 1, 0); + } + + SelectObject(hdc, old_font); + return ni; +} + + +extern bool qt_cleartype_enabled; +extern uint qt_pow_gamma[256]; + +QImage QFontEngineWin::alphaMapForGlyph(glyph_t glyph, const QTransform &xform) +{ + HFONT font = hfont; + if (qt_cleartype_enabled) { + LOGFONT lf = logfont; + lf.lfQuality = ANTIALIASED_QUALITY; + font = CreateFontIndirectW(&lf); + } + + QNativeImage *mask = drawGDIGlyph(font, glyph, 0, xform); + if (mask == 0) + return QImage(); + + QImage indexed(mask->width(), mask->height(), QImage::Format_Indexed8); + + // ### This part is kinda pointless, but we'll crash later if we dont because some + // code paths expects there to be colortables for index8-bit... + QVector colors(256); + for (int i=0; i<256; ++i) + colors[i] = qRgba(0, 0, 0, i); + indexed.setColorTable(colors); + + // Copy data... Cannot use QPainter here as GDI has messed up the + // Alpha channel of the ni.image pixels... + for (int y=0; yheight(); ++y) { + uchar *dest = indexed.scanLine(y); + if (mask->systemFormat() == QImage::Format_RGB16) { + const qint16 *src = (qint16 *) ((const QImage &) mask->image).scanLine(y); + for (int x=0; xwidth(); ++x) { +#ifdef Q_OS_WINCE + dest[x] = 255 - qGray(src[x]); +#else + dest[x] = 255 - (qt_pow_gamma[qGray(src[x])] * 255. / 2047.); +#endif + } + } else { + const uint *src = (uint *) ((const QImage &) mask->image).scanLine(y); + for (int x=0; xwidth(); ++x) { +#ifdef Q_OS_WINCE + dest[x] = 255 - qGray(src[x]); +#else + dest[x] = 255 - (qt_pow_gamma[qGray(src[x])] * 255. / 2047.); +#endif + } + } + } + + // Cleanup... + delete mask; + if (qt_cleartype_enabled) { + DeleteObject(font); + } + + return indexed; +} + +#define SPI_GETFONTSMOOTHINGCONTRAST 0x200C +#define SPI_SETFONTSMOOTHINGCONTRAST 0x200D + +QImage QFontEngineWin::alphaRGBMapForGlyph(glyph_t glyph, int margin, const QTransform &t) +{ + HFONT font = hfont; + + int contrast; + SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &contrast, 0); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) 1000, 0); + + QNativeImage *mask = drawGDIGlyph(font, glyph, margin, t); + SystemParametersInfo(SPI_SETFONTSMOOTHINGCONTRAST, 0, (void *) contrast, 0); + + if (mask == 0) + return QImage(); + + // Gracefully handle the odd case when the display is 16-bit + const QImage source = mask->image.depth() == 32 + ? mask->image + : mask->image.convertToFormat(QImage::Format_RGB32); + + QImage rgbMask(mask->width(), mask->height(), QImage::Format_RGB32); + for (int y=0; yheight(); ++y) { + uint *dest = (uint *) rgbMask.scanLine(y); + const uint *src = (uint *) source.scanLine(y); + for (int x=0; xwidth(); ++x) { + dest[x] = 0xffffffff - (0x00ffffff & src[x]); + } + } + + delete mask; + + return rgbMask; +} + +// -------------------------------------- Multi font engine + +QFontEngineMultiWin::QFontEngineMultiWin(QFontEngineWin *first, const QStringList &fallbacks) + : QFontEngineMulti(fallbacks.size()+1), + fallbacks(fallbacks) +{ + engines[0] = first; + first->ref.ref(); + fontDef = engines[0]->fontDef; +} + +void QFontEngineMultiWin::loadEngine(int at) +{ + Q_ASSERT(at < engines.size()); + Q_ASSERT(engines.at(at) == 0); + + QString fam = fallbacks.at(at-1); + + LOGFONT lf = static_cast(engines.at(0))->logfont; + HFONT hfont; + QT_WA({ + memcpy(lf.lfFaceName, fam.utf16(), sizeof(TCHAR)*qMin(fam.length()+1,32)); // 32 = Windows hard-coded + hfont = CreateFontIndirectW(&lf); + } , { + // LOGFONTA and LOGFONTW are binary compatible + QByteArray lname = fam.toLocal8Bit(); + memcpy(lf.lfFaceName,lname.data(), + qMin(lname.length()+1,32)); // 32 = Windows hard-coded + hfont = CreateFontIndirectA((LOGFONTA*)&lf); + }); + bool stockFont = false; + if (hfont == 0) { + hfont = (HFONT)GetStockObject(ANSI_VAR_FONT); + stockFont = true; + } + engines[at] = new QFontEngineWin(fam, hfont, stockFont, lf); + engines[at]->ref.ref(); + engines[at]->fontDef = fontDef; +} + +QT_END_NAMESPACE -- cgit v1.2.3