Allow using multi fontengine in QTextLayout with QRawFont.

This change enables us to instantiate a QFontEngineMulti that takes
the raw font's font engine as its primary engine but can use fallback
engines based on the platform. Since this can be quite expensive, we
defer the query for fallback families' names until it's needed and
we cache the resulting multi font engine.

Change-Id: I390dbc1cb2fe61d56867f29a03f313eb3eb49dc3
Reviewed-by: Simon Hausmann <simon.hausmann@nokia.com>
This commit is contained in:
Pierre Rossi 2012-01-16 17:40:21 +01:00 committed by Qt by Nokia
parent 10ac807085
commit cc78f47778
7 changed files with 108 additions and 22 deletions

View File

@ -2715,18 +2715,22 @@ QFontEngine *QFontCache::findEngine(const Key &key)
EngineCache::Iterator it = engineCache.find(key), EngineCache::Iterator it = engineCache.find(key),
end = engineCache.end(); end = engineCache.end();
if (it == end) return 0; if (it == end) return 0;
// found... update the hitcount and timestamp // found... update the hitcount and timestamp
it.value().hits++; updateHitCountAndTimeStamp(it.value());
it.value().timestamp = ++current_timestamp;
return it.value().data;
}
void QFontCache::updateHitCountAndTimeStamp(Engine &value)
{
value.hits++;
value.timestamp = ++current_timestamp;
FC_DEBUG("QFontCache: found font engine\n" FC_DEBUG("QFontCache: found font engine\n"
" %p: timestamp %4u hits %3u ref %2d/%2d, type '%s'", " %p: timestamp %4u hits %3u ref %2d/%2d, type '%s'",
it.value().data, it.value().timestamp, it.value().hits, value.data, value.timestamp, value.hits,
it.value().data->ref.load(), it.value().data->cache_count, value.data->ref.load(), value.data->cache_count,
it.value().data->name()); value.data->name());
return it.value().data;
} }
void QFontCache::removeEngine(QFontEngine *engine) void QFontCache::removeEngine(QFontEngine *engine)
@ -2743,14 +2747,17 @@ void QFontCache::removeEngine(QFontEngine *engine)
} }
} }
void QFontCache::insertEngine(const Key &key, QFontEngine *engine) void QFontCache::insertEngine(const Key &key, QFontEngine *engine, bool insertMulti)
{ {
FC_DEBUG("QFontCache: inserting new engine %p", engine); FC_DEBUG("QFontCache: inserting new engine %p", engine);
Engine data(engine); Engine data(engine);
data.timestamp = ++current_timestamp; data.timestamp = ++current_timestamp;
engineCache.insert(key, data); if (insertMulti)
engineCache.insertMulti(key, data);
else
engineCache.insert(key, data);
// only increase the cost if this is the first time we insert the engine // only increase the cost if this is the first time we insert the engine
if (engine->cache_count == 0) if (engine->cache_count == 0)

View File

@ -242,9 +242,10 @@ public:
EngineCache engineCache; EngineCache engineCache;
QFontEngine *findEngine(const Key &key); QFontEngine *findEngine(const Key &key);
void insertEngine(const Key &key, QFontEngine *engine);
void removeEngine(QFontEngine *engine);
void updateHitCountAndTimeStamp(Engine &value);
void insertEngine(const Key &key, QFontEngine *engine, bool insertMulti = false);
void removeEngine(QFontEngine *engine);
private: private:
void increaseCost(uint cost); void increaseCost(uint cost);

View File

@ -162,7 +162,6 @@ private:
friend class QFontEngineMultiXLFD; friend class QFontEngineMultiXLFD;
friend class QFontEngineMultiQWS; friend class QFontEngineMultiQWS;
friend class QFontEngineMultiQPA; friend class QFontEngineMultiQPA;
friend class QTextEngine;
#ifdef QT_BUILD_INTERNAL #ifdef QT_BUILD_INTERNAL
friend class ::tst_QFont; friend class ::tst_QFont;
#endif #endif

View File

@ -85,6 +85,7 @@ struct QGlyphLayout;
class Q_GUI_EXPORT QFontEngine : public QObject class Q_GUI_EXPORT QFontEngine : public QObject
{ {
Q_OBJECT
public: public:
enum Type { enum Type {
Box, Box,
@ -344,6 +345,7 @@ private:
class Q_GUI_EXPORT QFontEngineMulti : public QFontEngine class Q_GUI_EXPORT QFontEngineMulti : public QFontEngine
{ {
Q_OBJECT
public: public:
explicit QFontEngineMulti(int engineCount); explicit QFontEngineMulti(int engineCount);
~QFontEngineMulti(); ~QFontEngineMulti();

View File

@ -46,8 +46,10 @@
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtCore/QBuffer> #include <QtCore/QBuffer>
#include <QtGui/QPlatformFontDatabase>
#include <QtGui/private/qpaintengine_raster_p.h> #include <QtGui/private/qpaintengine_raster_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/QPlatformFontDatabase>
#include <QtGui/QPlatformIntegration>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -662,6 +664,20 @@ void QPAGenerator::writeTaggedQFixed(QFontEngineQPA::HeaderTag tag, QFixed value
QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script, const QStringList &fallbacks) QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script, const QStringList &fallbacks)
: QFontEngineMulti(fallbacks.size() + 1), : QFontEngineMulti(fallbacks.size() + 1),
fallbackFamilies(fallbacks), script(_script) fallbackFamilies(fallbacks), script(_script)
, fallbacksQueried(true)
{
init(fe);
}
QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script)
: QFontEngineMulti(2)
, script(_script)
, fallbacksQueried(false)
{
init(fe);
}
void QFontEngineMultiQPA::init(QFontEngine *fe)
{ {
Q_ASSERT(fe && fe->type() != QFontEngine::Multi); Q_ASSERT(fe && fe->type() != QFontEngine::Multi);
engines[0] = fe; engines[0] = fe;
@ -672,18 +688,73 @@ QFontEngineMultiQPA::QFontEngineMultiQPA(QFontEngine *fe, int _script, const QSt
void QFontEngineMultiQPA::loadEngine(int at) void QFontEngineMultiQPA::loadEngine(int at)
{ {
bool canLoadFallbackEngine = true;
if (!fallbacksQueried) {
// Original FontEngine to restore after the fill.
QFontEngine *fe = engines[0];
fallbackFamilies = QGuiApplicationPrivate::instance()->platformIntegration()->fontDatabase()->fallbacksForFamily(fe->fontDef.family, QFont::Style(fe->fontDef.style)
, QFont::AnyStyle, QUnicodeTables::Script(script));
if (fallbackFamilies.size() > 1) {
engines.fill(0, fallbackFamilies.size() + 1);
engines[0] = fe;
} else {
// Turns out we lied about having any fallback at all.
canLoadFallbackEngine = false;
engines[1] = fe;
}
fallbacksQueried = true;
}
Q_ASSERT(at < engines.size()); Q_ASSERT(at < engines.size());
Q_ASSERT(engines.at(at) == 0); Q_ASSERT(engines.at(at) == 0);
QFontDef request = fontDef; QFontDef request = fontDef;
request.styleStrategy |= QFont::NoFontMerging; if (canLoadFallbackEngine) {
request.family = fallbackFamilies.at(at-1); request.styleStrategy |= QFont::NoFontMerging;
engines[at] = QFontDatabase::findFont(script, request.family = fallbackFamilies.at(at-1);
/*fontprivate*/0, engines[at] = QFontDatabase::findFont(script,
request, false); /*fontprivate = */0,
request, /*multi = */false);
}
Q_ASSERT(engines[at]); Q_ASSERT(engines[at]);
engines[at]->ref.ref(); engines[at]->ref.ref();
engines[at]->fontDef = request; engines[at]->fontDef = request;
} }
/*
This is used indirectly by QtWebKit when using QTextLayout::setRawFont
The purpose of this is to provide the necessary font fallbacks when drawing complex
text. Since QtWebKit ends up repeatedly creating QTextLayout instances and passing them
the same raw font over and over again, we want to cache the corresponding multi font engine
as it may contain fallback font engines already.
*/
QFontEngine* QFontEngineMultiQPA::createMultiFontEngine(QFontEngine *fe, int script)
{
QFontEngine *engine = 0;
QFontCache::Key key(fe->fontDef, script, /*multi = */true);
QFontCache *fc = QFontCache::instance();
// We can't rely on the fontDef (and hence the cache Key)
// alone to distinguish webfonts, since these should not be
// accidentally shared, even if the resulting fontcache key
// is strictly identical. See:
// http://www.w3.org/TR/css3-fonts/#font-face-rule
const bool faceIsLocal = !fe->faceId().filename.isEmpty();
QFontCache::EngineCache::Iterator it = fc->engineCache.find(key),
end = fc->engineCache.end();
while (it != end && it.key() == key) {
QFontEngineMulti *cachedEngine = qobject_cast<QFontEngineMulti *>(it.value().data);
if (faceIsLocal || (cachedEngine && fe == cachedEngine->engine(0))) {
engine = cachedEngine;
fc->updateHitCountAndTimeStamp(it.value());
break;
}
it++;
}
if (!engine) {
engine = new QFontEngineMultiQPA(fe, script);
QFontCache::instance()->insertEngine(key, engine, /* insertMulti */ !faceIsLocal);
}
Q_ASSERT(engine);
return engine;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -249,13 +249,18 @@ public:
QFontEngineMultiQPA(QFontEngine *fe, int script, const QStringList &fallbacks); QFontEngineMultiQPA(QFontEngine *fe, int script, const QStringList &fallbacks);
void loadEngine(int at); void loadEngine(int at);
static QFontEngine* createMultiFontEngine(QFontEngine *fe, int script);
int fallbackFamilyCount() const { return fallbackFamilies.size(); } int fallbackFamilyCount() const { return fallbackFamilies.size(); }
QString fallbackFamilyAt(int at) const { return fallbackFamilies.at(at); } QString fallbackFamilyAt(int at) const { return fallbackFamilies.at(at); }
private: private:
QFontEngineMultiQPA(QFontEngine *fe, int script);
void init(QFontEngine *fe);
QStringList fallbackFamilies; QStringList fallbackFamilies;
int script; int script;
bool fallbacksQueried;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -59,6 +59,7 @@
#include <qinputmethod.h> #include <qinputmethod.h>
#include <stdlib.h> #include <stdlib.h>
#include "qfontengine_qpa_p.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -1708,7 +1709,7 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
if (feCache.prevFontEngine && feCache.prevFontEngine->type() == QFontEngine::Multi && feCache.prevScript == script) { if (feCache.prevFontEngine && feCache.prevFontEngine->type() == QFontEngine::Multi && feCache.prevScript == script) {
engine = feCache.prevFontEngine; engine = feCache.prevFontEngine;
} else { } else {
engine = QFontDatabase::findFont(script, /*fontPrivate*/0, rawFont.d->fontEngine->fontDef, true); engine = QFontEngineMultiQPA::createMultiFontEngine(rawFont.d->fontEngine, script);
feCache.prevFontEngine = engine; feCache.prevFontEngine = engine;
feCache.prevScript = script; feCache.prevScript = script;
engine->ref.ref(); engine->ref.ref();
@ -1720,7 +1721,7 @@ QFontEngine *QTextEngine::fontEngine(const QScriptItem &si, QFixed *ascent, QFix
scaledEngine = feCache.prevScaledFontEngine; scaledEngine = feCache.prevScaledFontEngine;
} else { } else {
QFontEngine *scEngine = rawFont.d->fontEngine->cloneWithSize(smallCapsFraction * rawFont.pixelSize()); QFontEngine *scEngine = rawFont.d->fontEngine->cloneWithSize(smallCapsFraction * rawFont.pixelSize());
scaledEngine = QFontDatabase::findFont(script, /*fontPrivate*/0, scEngine->fontDef, true); scaledEngine = QFontEngineMultiQPA::createMultiFontEngine(scEngine, script);
scaledEngine->ref.ref(); scaledEngine->ref.ref();
feCache.prevScaledFontEngine = scaledEngine; feCache.prevScaledFontEngine = scaledEngine;
} }