Teach font database to populate families lazily
Instead of requiring that QPlatformFontDatabase::populateFontDatabase() populates every single font in the system by calling registerFont(), we now allow the platform database to call registerFontFamily() instead, and then keep track of which families we've yet to fully populate in the font database. Once a property of a family is requested (such as its writing system, style, etc), the family is lazily populated by calling back to the platform database through QPlatformFontDatabase::populateFamily(), which in turn does the final call to registerFont() as before. This cuts application startup on OS X and iOS (of which the font population used to be a major limiting factor) from roughly one second to about 350ms. Task-number: QTBUG-37165 Change-Id: Ic2fc3447beb818ffe23635a5b7816ed7e70c93a7 Reviewed-by: Konstantin Ritt <ritt.ks@gmail.com> Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com> Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
parent
7a08f714a5
commit
2bc7a40048
@ -317,6 +317,7 @@ struct QtFontFamily
|
||||
|
||||
QtFontFamily(const QString &n)
|
||||
:
|
||||
populated(false),
|
||||
fixedPitch(false),
|
||||
name(n), count(0), foundries(0)
|
||||
, bogusWritingSystems(false)
|
||||
@ -330,6 +331,7 @@ struct QtFontFamily
|
||||
free(foundries);
|
||||
}
|
||||
|
||||
bool populated : 1;
|
||||
bool fixedPitch : 1;
|
||||
|
||||
QString name;
|
||||
@ -344,6 +346,8 @@ struct QtFontFamily
|
||||
|
||||
bool matchesFamilyName(const QString &familyName) const;
|
||||
QtFontFoundry *foundry(const QString &f, bool = false);
|
||||
|
||||
void ensurePopulated();
|
||||
};
|
||||
|
||||
QtFontFoundry *QtFontFamily::foundry(const QString &f, bool create)
|
||||
@ -375,6 +379,14 @@ bool QtFontFamily::matchesFamilyName(const QString &familyName) const
|
||||
return name.compare(familyName, Qt::CaseInsensitive) == 0 || aliases.contains(familyName, Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
void QtFontFamily::ensurePopulated()
|
||||
{
|
||||
if (populated)
|
||||
return;
|
||||
|
||||
QGuiApplicationPrivate::platformIntegration()->fontDatabase()->populateFamily(name);
|
||||
Q_ASSERT(populated);
|
||||
}
|
||||
|
||||
class QFontDatabasePrivate
|
||||
{
|
||||
@ -386,7 +398,14 @@ public:
|
||||
~QFontDatabasePrivate() {
|
||||
free();
|
||||
}
|
||||
QtFontFamily *family(const QString &f, bool = false);
|
||||
|
||||
enum FamilyRequestFlags {
|
||||
RequestFamily = 0,
|
||||
EnsureCreated,
|
||||
EnsurePopulated
|
||||
};
|
||||
|
||||
QtFontFamily *family(const QString &f, FamilyRequestFlags flags = EnsurePopulated);
|
||||
void free() {
|
||||
while (count--)
|
||||
delete families[count];
|
||||
@ -424,8 +443,10 @@ void QFontDatabasePrivate::invalidate()
|
||||
emit static_cast<QGuiApplication *>(QCoreApplication::instance())->fontDatabaseChanged();
|
||||
}
|
||||
|
||||
QtFontFamily *QFontDatabasePrivate::family(const QString &f, bool create)
|
||||
QtFontFamily *QFontDatabasePrivate::family(const QString &f, FamilyRequestFlags flags)
|
||||
{
|
||||
QtFontFamily *fam = 0;
|
||||
|
||||
int low = 0;
|
||||
int high = count;
|
||||
int pos = count / 2;
|
||||
@ -439,28 +460,34 @@ QtFontFamily *QFontDatabasePrivate::family(const QString &f, bool create)
|
||||
pos = (high + low) / 2;
|
||||
}
|
||||
if (!res)
|
||||
return families[pos];
|
||||
}
|
||||
if (!create)
|
||||
return 0;
|
||||
|
||||
if (res < 0)
|
||||
pos++;
|
||||
|
||||
// qDebug() << "adding family " << f.toLatin1() << " at " << pos << " total=" << count;
|
||||
if (!(count % 8)) {
|
||||
QtFontFamily **newFamilies = (QtFontFamily **)
|
||||
realloc(families,
|
||||
(((count+8) >> 3) << 3) * sizeof(QtFontFamily *));
|
||||
Q_CHECK_PTR(newFamilies);
|
||||
families = newFamilies;
|
||||
fam = families[pos];
|
||||
}
|
||||
|
||||
QtFontFamily *family = new QtFontFamily(f);
|
||||
memmove(families + pos + 1, families + pos, (count-pos)*sizeof(QtFontFamily *));
|
||||
families[pos] = family;
|
||||
count++;
|
||||
return families[pos];
|
||||
if (!fam && (flags & EnsureCreated)) {
|
||||
if (res < 0)
|
||||
pos++;
|
||||
|
||||
// qDebug() << "adding family " << f.toLatin1() << " at " << pos << " total=" << count;
|
||||
if (!(count % 8)) {
|
||||
QtFontFamily **newFamilies = (QtFontFamily **)
|
||||
realloc(families,
|
||||
(((count+8) >> 3) << 3) * sizeof(QtFontFamily *));
|
||||
Q_CHECK_PTR(newFamilies);
|
||||
families = newFamilies;
|
||||
}
|
||||
|
||||
QtFontFamily *family = new QtFontFamily(f);
|
||||
memmove(families + pos + 1, families + pos, (count-pos)*sizeof(QtFontFamily *));
|
||||
families[pos] = family;
|
||||
count++;
|
||||
|
||||
fam = families[pos];
|
||||
}
|
||||
|
||||
if (fam && (flags & EnsurePopulated))
|
||||
fam->ensurePopulated();
|
||||
|
||||
return fam;
|
||||
}
|
||||
|
||||
|
||||
@ -670,7 +697,7 @@ void qt_registerFont(const QString &familyName, const QString &stylename,
|
||||
styleKey.style = style;
|
||||
styleKey.weight = weight;
|
||||
styleKey.stretch = stretch;
|
||||
QtFontFamily *f = d->family(familyName, true);
|
||||
QtFontFamily *f = d->family(familyName, QFontDatabasePrivate::EnsureCreated);
|
||||
f->fixedPitch = fixedPitch;
|
||||
|
||||
for (int i = 0; i < QFontDatabase::WritingSystemsCount; ++i) {
|
||||
@ -689,6 +716,13 @@ void qt_registerFont(const QString &familyName, const QString &stylename,
|
||||
integration->fontDatabase()->releaseHandle(size->handle);
|
||||
}
|
||||
size->handle = handle;
|
||||
f->populated = true;
|
||||
}
|
||||
|
||||
void qt_registerFontFamily(const QString &familyName)
|
||||
{
|
||||
// Create uninitialized/unpopulated family
|
||||
privateDb()->family(familyName, QFontDatabasePrivate::EnsureCreated);
|
||||
}
|
||||
|
||||
void qt_registerAliasToFontFamily(const QString &familyName, const QString &alias)
|
||||
@ -697,7 +731,7 @@ void qt_registerAliasToFontFamily(const QString &familyName, const QString &alia
|
||||
return;
|
||||
|
||||
QFontDatabasePrivate *d = privateDb();
|
||||
QtFontFamily *f = d->family(familyName, false);
|
||||
QtFontFamily *f = d->family(familyName, QFontDatabasePrivate::RequestFamily);
|
||||
if (!f)
|
||||
return;
|
||||
|
||||
@ -1092,6 +1126,8 @@ static int match(int script, const QFontDef &request,
|
||||
if (!matchFamilyName(family_name, test.family))
|
||||
continue;
|
||||
|
||||
test.family->ensurePopulated();
|
||||
|
||||
if (family_name.isEmpty())
|
||||
load(test.family->name, script);
|
||||
|
||||
@ -1304,6 +1340,8 @@ QList<QFontDatabase::WritingSystem> QFontDatabase::writingSystems() const
|
||||
QList<WritingSystem> list;
|
||||
for (int i = 0; i < d->count; ++i) {
|
||||
QtFontFamily *family = d->families[i];
|
||||
family->ensurePopulated();
|
||||
|
||||
if (family->count == 0)
|
||||
continue;
|
||||
for (int x = Latin; x < WritingSystemsCount; ++x) {
|
||||
@ -1367,11 +1405,14 @@ QStringList QFontDatabase::families(WritingSystem writingSystem) const
|
||||
QStringList flist;
|
||||
for (int i = 0; i < d->count; i++) {
|
||||
QtFontFamily *f = d->families[i];
|
||||
if (f->count == 0)
|
||||
if (f->populated && f->count == 0)
|
||||
continue;
|
||||
if (writingSystem != Any && (f->writingSystems[writingSystem] != QtFontFamily::Supported))
|
||||
continue;
|
||||
if (f->count == 1) {
|
||||
if (writingSystem != Any) {
|
||||
f->ensurePopulated();
|
||||
if (f->writingSystems[writingSystem] != QtFontFamily::Supported)
|
||||
continue;
|
||||
}
|
||||
if (!f->populated || f->count == 1) {
|
||||
flist.append(f->name);
|
||||
} else {
|
||||
for (int j = 0; j < f->count; j++) {
|
||||
|
@ -53,6 +53,7 @@ void qt_registerFont(const QString &familyname, const QString &stylename,
|
||||
bool scalable, int pixelSize, bool fixedPitch,
|
||||
const QSupportedWritingSystems &writingSystems, void *hanlde);
|
||||
|
||||
void qt_registerFontFamily(const QString &familyName);
|
||||
void qt_registerAliasToFontFamily(const QString &familyName, const QString &alias);
|
||||
|
||||
/*!
|
||||
@ -118,7 +119,7 @@ void QPlatformFontDatabase::registerQPF2Font(const QByteArray &dataArray, void *
|
||||
The writing systems supported by the font are specified by the
|
||||
\a writingSystems argument.
|
||||
|
||||
\sa registerQPF2Font()
|
||||
\sa registerQPF2Font(), registerFontFamily()
|
||||
*/
|
||||
void QPlatformFontDatabase::registerFont(const QString &familyname, const QString &stylename,
|
||||
const QString &foundryname, QFont::Weight weight,
|
||||
@ -134,6 +135,18 @@ void QPlatformFontDatabase::registerFont(const QString &familyname, const QStrin
|
||||
fixedPitch, writingSystems, usrPtr);
|
||||
}
|
||||
|
||||
/*!
|
||||
Registers a font family with the font database. The font will be
|
||||
lazily populated by a callback to populateFamily() when the font
|
||||
database determines that the family needs population.
|
||||
|
||||
\sa populateFamily(), registerFont()
|
||||
*/
|
||||
void QPlatformFontDatabase::registerFontFamily(const QString &familyName)
|
||||
{
|
||||
qt_registerFontFamily(familyName);
|
||||
}
|
||||
|
||||
class QWritingSystemsPrivate
|
||||
{
|
||||
public:
|
||||
@ -249,6 +262,11 @@ QPlatformFontDatabase::~QPlatformFontDatabase()
|
||||
Reimplement this function in a subclass for a convenient place to initialize
|
||||
the internal font database.
|
||||
|
||||
You may lazily populate the database by calling registerFontFamily() instead
|
||||
of registerFont(), in which case you'll get a callback to populateFamily()
|
||||
when the required family needs population. You then call registerFont() to
|
||||
finish population of the family.
|
||||
|
||||
The default implementation looks in the fontDir() location and registers all
|
||||
QPF2 fonts.
|
||||
*/
|
||||
@ -275,6 +293,18 @@ void QPlatformFontDatabase::populateFontDatabase()
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
This function is called whenever a lazily populated family, populated
|
||||
through registerFontFamily(), needs full population.
|
||||
|
||||
You are expected to fully populate the family by calling registerFont()
|
||||
for each font that matches the family name.
|
||||
*/
|
||||
void QPlatformFontDatabase::populateFamily(const QString &familyName)
|
||||
{
|
||||
Q_UNUSED(familyName);
|
||||
}
|
||||
|
||||
/*!
|
||||
This function is called whenever the font database is invalidated.
|
||||
|
||||
|
@ -96,6 +96,7 @@ class Q_GUI_EXPORT QPlatformFontDatabase
|
||||
public:
|
||||
virtual ~QPlatformFontDatabase();
|
||||
virtual void populateFontDatabase();
|
||||
virtual void populateFamily(const QString &familyName);
|
||||
virtual void invalidate();
|
||||
|
||||
virtual QFontEngineMulti *fontEngineMulti(QFontEngine *fontEngine, QChar::Script script);
|
||||
@ -125,6 +126,7 @@ public:
|
||||
bool scalable, int pixelSize, bool fixedPitch,
|
||||
const QSupportedWritingSystems &writingSystems, void *handle);
|
||||
|
||||
static void registerFontFamily(const QString &familyName);
|
||||
static void registerAliasToFontFamily(const QString &familyName, const QString &alias);
|
||||
};
|
||||
|
||||
|
@ -41,9 +41,11 @@
|
||||
|
||||
#include "qglobal.h"
|
||||
|
||||
#ifdef Q_OS_MACX
|
||||
#if defined(Q_OS_MACX)
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <IOKit/graphics/IOGraphicsLib.h>
|
||||
#elif defined(Q_OS_IOS)
|
||||
#import <UIKit/UIFont.h>
|
||||
#endif
|
||||
|
||||
#include "qcoretextfontdatabase_p.h"
|
||||
@ -176,29 +178,50 @@ QCoreTextFontDatabase::~QCoreTextFontDatabase()
|
||||
{
|
||||
}
|
||||
|
||||
static CFArrayRef availableFamilyNames()
|
||||
{
|
||||
#if defined(Q_OS_OSX)
|
||||
return CTFontManagerCopyAvailableFontFamilyNames();
|
||||
#elif defined(Q_OS_IOS)
|
||||
return (CFArrayRef) [[UIFont familyNames] retain];
|
||||
#endif
|
||||
}
|
||||
|
||||
void QCoreTextFontDatabase::populateFontDatabase()
|
||||
{
|
||||
// The caller (QFontDB) expects the db to be populate only with system fonts, so we need
|
||||
// to make sure that any previously registered app fonts become invisible.
|
||||
removeApplicationFonts();
|
||||
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
QCFType<CFArrayRef> familyNames = availableFamilyNames();
|
||||
const int numberOfFamilies = CFArrayGetCount(familyNames);
|
||||
for (int i = 0; i < numberOfFamilies; ++i) {
|
||||
QString familyName = QCFString::toQString((CFStringRef) CFArrayGetValueAtIndex(familyNames, i));
|
||||
|
||||
QCFType<CTFontCollectionRef> collection = CTFontCollectionCreateFromAvailableFonts(0);
|
||||
if (! collection)
|
||||
// Don't populate internal fonts
|
||||
if (familyName.startsWith(QLatin1Char('.')) || familyName == QStringLiteral("LastResort"))
|
||||
continue;
|
||||
|
||||
QPlatformFontDatabase::registerFontFamily(familyName);
|
||||
}
|
||||
}
|
||||
|
||||
void QCoreTextFontDatabase::populateFamily(const QString &familyName)
|
||||
{
|
||||
CFMutableDictionaryRef attributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
CFDictionaryAddValue(attributes, kCTFontFamilyNameAttribute, QCFString(familyName));
|
||||
CTFontDescriptorRef nameOnlyDescriptor = CTFontDescriptorCreateWithAttributes(attributes);
|
||||
|
||||
// A single family might match several different fonts with different styles eg.
|
||||
QCFType<CFArrayRef> matchingFonts = (CFArrayRef) CTFontDescriptorCreateMatchingFontDescriptors(nameOnlyDescriptor, 0);
|
||||
if (!matchingFonts) {
|
||||
qWarning() << "QCoreTextFontDatabase: Found no matching fonts for family" << familyName;
|
||||
return;
|
||||
|
||||
QCFType<CFArrayRef> fonts = CTFontCollectionCreateMatchingFontDescriptors(collection);
|
||||
if (! fonts)
|
||||
return;
|
||||
|
||||
const int numFonts = CFArrayGetCount(fonts);
|
||||
for (int i = 0; i < numFonts; ++i) {
|
||||
CTFontDescriptorRef font = (CTFontDescriptorRef) CFArrayGetValueAtIndex(fonts, i);
|
||||
populateFromDescriptor(font);
|
||||
}
|
||||
|
||||
[pool release];
|
||||
const int numFonts = CFArrayGetCount(matchingFonts);
|
||||
for (int i = 0; i < numFonts; ++i)
|
||||
populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i)));
|
||||
}
|
||||
|
||||
void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font)
|
||||
|
@ -72,6 +72,7 @@ public:
|
||||
QCoreTextFontDatabase();
|
||||
~QCoreTextFontDatabase();
|
||||
void populateFontDatabase();
|
||||
void populateFamily(const QString &familyName) Q_DECL_OVERRIDE;
|
||||
|
||||
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle);
|
||||
QFontEngine *fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference);
|
||||
|
Loading…
Reference in New Issue
Block a user