diff --git a/src/servers/app/GlyphLayoutEngine.h b/src/servers/app/GlyphLayoutEngine.h index a2e54edc42..899f3cfe0b 100644 --- a/src/servers/app/GlyphLayoutEngine.h +++ b/src/servers/app/GlyphLayoutEngine.h @@ -19,43 +19,69 @@ #include class FontCacheReference { - public: - FontCacheReference() - : fCacheEntry(NULL) - , fWriteLocked(false) - { - } - ~FontCacheReference() - { - if (fCacheEntry) { - if (fWriteLocked) - fCacheEntry->WriteUnlock(); - else - fCacheEntry->ReadUnlock(); - - FontCache::Default()->Recycle( - fCacheEntry); - } - } - void SetTo(FontCacheEntry* entry, - bool writeLocked) - { - fCacheEntry = entry; - fWriteLocked = writeLocked; - } - inline FontCacheEntry* Entry() const - { return fCacheEntry; } - inline bool WriteLocked() const - { return fWriteLocked; } +public: + FontCacheReference() + : + fCacheEntry(NULL), + fWriteLocked(false) + { + } - private: + ~FontCacheReference() + { + Unset(); + } + + void SetTo(FontCacheEntry* entry, bool writeLocked) + { + // NOTE: If the semantics are changed such + // that the reference to a previous entry + // is properly released, then don't forget + // to adapt existing which transfers + // responsibility of entries between + // references! + fCacheEntry = entry; + fWriteLocked = writeLocked; + } + + void Unset() + { + if (fCacheEntry == NULL) + return; + + if (fWriteLocked) + fCacheEntry->WriteUnlock(); + else + fCacheEntry->ReadUnlock(); + + FontCache::Default()->Recycle(fCacheEntry); + } + + inline FontCacheEntry* Entry() const + { + return fCacheEntry; + } + + inline bool WriteLocked() const + { + return fWriteLocked; + } + +private: FontCacheEntry* fCacheEntry; bool fWriteLocked; }; class GlyphLayoutEngine { - public: +public: + static bool IsWhiteSpace(uint32 glyphCode); + + static FontCacheEntry* FontCacheEntryFor(const ServerFont& font, + const FontCacheEntry* disallowedEntry, + const char* utf8String, int32 length, + FontCacheReference& cacheReference, + bool needsWriteLock); template static bool LayoutGlyphs(GlyphConsumer& consumer, @@ -68,15 +94,12 @@ class GlyphLayoutEngine { const BPoint* offsets = NULL, FontCacheReference* cacheReference = NULL); - static bool IsWhiteSpace(uint32 glyphCode); - - private: +private: GlyphLayoutEngine(); virtual ~GlyphLayoutEngine(); }; -// IsWhiteSpace inline bool GlyphLayoutEngine::IsWhiteSpace(uint32 charCode) { @@ -97,42 +120,118 @@ GlyphLayoutEngine::IsWhiteSpace(uint32 charCode) } -// LayoutGlyphs +inline FontCacheEntry* +GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font, + const FontCacheEntry* disallowedEntry, const char* utf8String, int32 length, + FontCacheReference& cacheReference, bool needsWriteLock) +{ + ASSERT(cacheReference.Entry() == NULL); + + FontCache* cache = FontCache::Default(); + FontCacheEntry* entry = cache->FontCacheEntryFor(font); + if (entry == NULL) + return NULL; + + if (entry == disallowedEntry) { + cache->Recycle(entry); + return NULL; + } + + if (needsWriteLock) { + if (!entry->WriteLock()) { + cache->Recycle(entry); + return NULL; + } + } else { + if (!entry->ReadLock()) { + cache->Recycle(entry); + return NULL; + } + } + + // At this point, we have a valid FontCacheEntry and it is locked in the + // proper mode. We can setup the FontCacheReference so it takes care of + // the locking and recycling from now and return the entry. + cacheReference.SetTo(entry, needsWriteLock); + return entry; +} + + template inline bool GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, const ServerFont& font, const char* utf8String, int32 length, const escapement_delta* delta, bool kerning, uint8 spacing, - const BPoint* offsets, FontCacheReference* cacheReference) + const BPoint* offsets, FontCacheReference* _cacheReference) { // TODO: implement spacing modes - FontCacheEntry* entry = NULL; + FontCacheReference cacheReference; FontCacheEntry* fallbackEntry = NULL; - bool needsWriteLock = false; - if (cacheReference) { - entry = cacheReference->Entry(); - needsWriteLock = cacheReference->WriteLocked(); + FontCacheReference fallbackCacheReference; + if (_cacheReference != NULL) { + entry = _cacheReference->Entry(); + // When there is already a cacheReference, it means there was already + // an iteration over the glyphs. The use-case is for example to do + // a layout pass to get the string width for the bounding box, then a + // second layout pass to actually render the glyphs to the screen. + // This means that the fallback entry mechanism will not do any good + // for the second pass, since the fallback glyphs have been stored in + // the original entry. } - if (!entry) { - FontCache* cache = FontCache::Default(); - entry = cache->FontCacheEntryFor(font); - - if (!entry || !entry->ReadLock()) { - cache->Recycle(entry); + if (entry == NULL) { + entry = FontCacheEntryFor(font, NULL, utf8String, length, + cacheReference, false); + + if (entry == NULL) return false; - } - - needsWriteLock = !entry->HasGlyphs(utf8String, length); - + + // See if the entry already has the glyphs cached. Switch to a write + // lock if not. + bool needsWriteLock = !entry->HasGlyphs(utf8String, length); if (needsWriteLock) { + // This also means we need the fallback font, since potentially, + // we have to obtain missing glyphs from it. We need to obtain + // the fallback font while we have not locked anything, since + // locking the FontManager with the write-lock held can obvisouly + // lead to a deadlock. + + cacheReference.SetTo(NULL, false); entry->ReadUnlock(); - if (!entry->WriteLock()) { - cache->Recycle(entry); - return false; + + if (gFontManager->Lock()) { + // TODO: We always get the fallback glyphs from VL Gothic at the + // moment, but of course the fallback font should a) contain the + // missing glyphs at all and b) be similar to the original font. + // So there should be a mapping of some kind to know the most + // suitable fallback font. + FontStyle* fallbackStyle = gFontManager->GetStyleByIndex( + "VL Gothic", 0); + if (fallbackStyle != NULL) { + ServerFont fallbackFont(*fallbackStyle, font.Size()); + gFontManager->Unlock(); + // Force the write-lock on the fallback entry, since we + // don't transfer or copy GlyphCache objects from one cache + // to the other, but create new glyphs which are stored in + // "entry" in any case, which requires the write cache for + // sure (used FontEngine of fallbackEntry). + fallbackEntry = FontCacheEntryFor(fallbackFont, entry, + utf8String, length, fallbackCacheReference, true); + // NOTE: We don't care if fallbackEntry is NULL, fetching + // alternate glyphs will simply not work. + } else + gFontManager->Unlock(); } + + if (!entry->WriteLock()) { + FontCache::Default()->Recycle(entry); + return NULL; + } + + // Update the FontCacheReference, since the locking kind changed. + cacheReference.SetTo(entry, needsWriteLock); } } // else the entry was already used and is still locked @@ -172,66 +271,16 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, x += IsWhiteSpace(charCode) ? delta->space : delta->nonspace; } - const GlyphCache* glyph = entry->Glyph(charCode); + const GlyphCache* glyph = entry->Glyph(charCode, fallbackEntry); if (glyph == NULL) { - // Try to find a suitable glyph in another font - FontCache* cache = FontCache::Default(); - bool needsWriteLock = false; - ServerFont fallbackFont(*(gFontManager->GetStyleByIndex("VL Gothic", - 0))); - // We always try to get the glyph from VL Gothic, so we can - // display japanese characters. Other scripts (indian, ...) - // should be handled too, perhaps with a charcode > font - // mapping. - // TODO : the font should not be hardcoded, but somehow derived - // from the one that missed a glyph. - bool consumed = true; - fallbackEntry = cache->FontCacheEntryFor(fallbackFont); - if (fallbackEntry != entry) - { - if (!fallbackEntry || !fallbackEntry->ReadLock()) { - cache->Recycle(fallbackEntry); - continue; - } - - needsWriteLock = !fallbackEntry->HasGlyphs(utf8String, length); - - if (needsWriteLock) { - fallbackEntry->ReadUnlock(); - if (!fallbackEntry->WriteLock()) { - cache->Recycle(fallbackEntry); - continue; - } - } - - glyph = fallbackEntry->Glyph(charCode); - if (glyph != NULL && !consumer.ConsumeGlyph(index, charCode, - glyph, fallbackEntry, x, y)) { - advanceX = 0; - advanceY = 0; - consumed = false; - } - - if (needsWriteLock) - fallbackEntry->WriteUnlock(); - else - fallbackEntry->ReadUnlock(); - } - FontCache::Default()->Recycle(fallbackEntry); - - if (glyph == NULL) { - consumer.ConsumeEmptyGlyph(index, charCode, x, y); - continue; - } - - if (!consumed) + consumer.ConsumeEmptyGlyph(index, charCode, x, y); + continue; + } else { + if (!consumer.ConsumeGlyph(index, charCode, glyph, entry, x, y)) { + advanceX = 0; + advanceY = 0; break; - } - - if (!consumer.ConsumeGlyph(index, charCode, glyph, entry, x, y)) { - advanceX = 0; - advanceY = 0; - break; + } } // get next increment for pen position @@ -248,17 +297,15 @@ GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, y += advanceY; consumer.Finish(x, y); - if (!cacheReference) { - if (needsWriteLock) - entry->WriteUnlock(); - else - entry->ReadUnlock(); - - FontCache::Default()->Recycle(entry); - } else { - // the reference will take care of locking - if (!cacheReference->Entry()) - cacheReference->SetTo(entry, needsWriteLock); + if (_cacheReference != NULL && _cacheReference->Entry() == NULL) { + // The caller passed a FontCacheReference, but this is the first + // iteration -> switch the ownership from the stack allocated + // FontCacheReference to the one passed by the caller. The fallback + // FontCacheReference is not affected by this. Eventually, the fallback + // glyphs shall be cached in the original FontCacheEntry, so that it is + // not even used in this situation. + _cacheReference->SetTo(entry, cacheReference.WriteLocked()); + cacheReference.SetTo(NULL, false); } return true; }