1f4f30311SClemens Zeidler /* 2f4f30311SClemens Zeidler * Copyright 2007, Haiku. All rights reserved. 3f4f30311SClemens Zeidler * Distributed under the terms of the MIT License. 4f4f30311SClemens Zeidler * 5f4f30311SClemens Zeidler * Authors: 6f4f30311SClemens Zeidler * Stephan Aßmus <superstippi@gmx.de> 7f4f30311SClemens Zeidler */ 8f4f30311SClemens Zeidler 9f4f30311SClemens Zeidler #ifndef GLYPH_LAYOUT_ENGINE_H 10f4f30311SClemens Zeidler #define GLYPH_LAYOUT_ENGINE_H 11f4f30311SClemens Zeidler 12f4f30311SClemens Zeidler #include "utf8_functions.h" 13f4f30311SClemens Zeidler 14f4f30311SClemens Zeidler #include "FontCache.h" 15f4f30311SClemens Zeidler #include "FontCacheEntry.h" 16f4f30311SClemens Zeidler #include "FontManager.h" 17f4f30311SClemens Zeidler #include "ServerFont.h" 18f4f30311SClemens Zeidler 19f4f30311SClemens Zeidler #include <Debug.h> 20f4f30311SClemens Zeidler 21f4f30311SClemens Zeidler #include <ctype.h> 22f4f30311SClemens Zeidler 23f4f30311SClemens Zeidler class FontCacheReference { 24f4f30311SClemens Zeidler public: 25f4f30311SClemens Zeidler FontCacheReference() 26f4f30311SClemens Zeidler : 27f4f30311SClemens Zeidler fCacheEntry(NULL), 28f4f30311SClemens Zeidler fWriteLocked(false) 29f4f30311SClemens Zeidler { 30f4f30311SClemens Zeidler } 31f4f30311SClemens Zeidler 32f4f30311SClemens Zeidler ~FontCacheReference() 33f4f30311SClemens Zeidler { 34f4f30311SClemens Zeidler Unset(); 35f4f30311SClemens Zeidler } 36f4f30311SClemens Zeidler 37f4f30311SClemens Zeidler void SetTo(FontCacheEntry* entry, bool writeLocked) 38f4f30311SClemens Zeidler { 39f4f30311SClemens Zeidler // NOTE: If the semantics are changed such 40f4f30311SClemens Zeidler // that the reference to a previous entry 41f4f30311SClemens Zeidler // is properly released, then don't forget 42f4f30311SClemens Zeidler // to adapt existing which transfers 43f4f30311SClemens Zeidler // responsibility of entries between 44f4f30311SClemens Zeidler // references! 45f4f30311SClemens Zeidler fCacheEntry = entry; 46f4f30311SClemens Zeidler fWriteLocked = writeLocked; 47f4f30311SClemens Zeidler } 48f4f30311SClemens Zeidler 49f4f30311SClemens Zeidler void Unset() 50f4f30311SClemens Zeidler { 51f4f30311SClemens Zeidler if (fCacheEntry == NULL) 52f4f30311SClemens Zeidler return; 53f4f30311SClemens Zeidler 54f4f30311SClemens Zeidler if (fWriteLocked) 55f4f30311SClemens Zeidler fCacheEntry->WriteUnlock(); 56f4f30311SClemens Zeidler else 57f4f30311SClemens Zeidler fCacheEntry->ReadUnlock(); 58f4f30311SClemens Zeidler 59f4f30311SClemens Zeidler FontCache::Default()->Recycle(fCacheEntry); 60f4f30311SClemens Zeidler } 61f4f30311SClemens Zeidler 62f4f30311SClemens Zeidler inline FontCacheEntry* Entry() const 63f4f30311SClemens Zeidler { 64f4f30311SClemens Zeidler return fCacheEntry; 65f4f30311SClemens Zeidler } 66f4f30311SClemens Zeidler 67f4f30311SClemens Zeidler inline bool WriteLocked() const 68f4f30311SClemens Zeidler { 69f4f30311SClemens Zeidler return fWriteLocked; 70f4f30311SClemens Zeidler } 71f4f30311SClemens Zeidler 72f4f30311SClemens Zeidler private: 73f4f30311SClemens Zeidler FontCacheEntry* fCacheEntry; 74f4f30311SClemens Zeidler bool fWriteLocked; 75f4f30311SClemens Zeidler }; 76f4f30311SClemens Zeidler 77f4f30311SClemens Zeidler 78f4f30311SClemens Zeidler class GlyphLayoutEngine { 79f4f30311SClemens Zeidler public: 80f4f30311SClemens Zeidler static bool IsWhiteSpace(uint32 glyphCode); 81f4f30311SClemens Zeidler 82f4f30311SClemens Zeidler static FontCacheEntry* FontCacheEntryFor(const ServerFont& font, 83750958b8SStephan Aßmus bool forceVector, 84f4f30311SClemens Zeidler const FontCacheEntry* disallowedEntry, 85f4f30311SClemens Zeidler const char* utf8String, int32 length, 86f4f30311SClemens Zeidler FontCacheReference& cacheReference, 87f4f30311SClemens Zeidler bool needsWriteLock); 88f4f30311SClemens Zeidler 89f4f30311SClemens Zeidler template<class GlyphConsumer> 90f4f30311SClemens Zeidler static bool LayoutGlyphs(GlyphConsumer& consumer, 91f4f30311SClemens Zeidler const ServerFont& font, 92f4f30311SClemens Zeidler const char* utf8String, 93f4f30311SClemens Zeidler int32 length, 94f4f30311SClemens Zeidler const escapement_delta* delta = NULL, 95f4f30311SClemens Zeidler bool kerning = true, 96f4f30311SClemens Zeidler uint8 spacing = B_BITMAP_SPACING, 97f4f30311SClemens Zeidler const BPoint* offsets = NULL, 98f4f30311SClemens Zeidler FontCacheReference* cacheReference = NULL); 99f4f30311SClemens Zeidler 100f4f30311SClemens Zeidler private: 101f4f30311SClemens Zeidler static bool _WriteLockAndAcquireFallbackEntry( 102f4f30311SClemens Zeidler FontCacheReference& cacheReference, 103f4f30311SClemens Zeidler FontCacheEntry* entry, 104750958b8SStephan Aßmus const ServerFont& font, bool needsVector, 105f4f30311SClemens Zeidler const char* utf8String, int32 length, 106f4f30311SClemens Zeidler FontCacheReference& fallbackCacheReference, 107f4f30311SClemens Zeidler FontCacheEntry*& fallbackEntry); 108f4f30311SClemens Zeidler 109f4f30311SClemens Zeidler GlyphLayoutEngine(); 110f4f30311SClemens Zeidler virtual ~GlyphLayoutEngine(); 111f4f30311SClemens Zeidler }; 112f4f30311SClemens Zeidler 113f4f30311SClemens Zeidler 114f4f30311SClemens Zeidler inline bool 115f4f30311SClemens Zeidler GlyphLayoutEngine::IsWhiteSpace(uint32 charCode) 116f4f30311SClemens Zeidler { 117f4f30311SClemens Zeidler switch (charCode) { 118f4f30311SClemens Zeidler case 0x0009: /* tab */ 119f4f30311SClemens Zeidler case 0x000b: /* vertical tab */ 120f4f30311SClemens Zeidler case 0x000c: /* form feed */ 121f4f30311SClemens Zeidler case 0x0020: /* space */ 122f4f30311SClemens Zeidler case 0x00a0: /* non breaking space */ 123f4f30311SClemens Zeidler case 0x000a: /* line feed */ 124f4f30311SClemens Zeidler case 0x000d: /* carriage return */ 125f4f30311SClemens Zeidler case 0x2028: /* line separator */ 126f4f30311SClemens Zeidler case 0x2029: /* paragraph separator */ 127f4f30311SClemens Zeidler return true; 128f4f30311SClemens Zeidler } 129f4f30311SClemens Zeidler 130f4f30311SClemens Zeidler return false; 131f4f30311SClemens Zeidler } 132f4f30311SClemens Zeidler 133f4f30311SClemens Zeidler 134f4f30311SClemens Zeidler inline FontCacheEntry* 135750958b8SStephan Aßmus GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font, bool forceVector, 136f4f30311SClemens Zeidler const FontCacheEntry* disallowedEntry, const char* utf8String, int32 length, 137f4f30311SClemens Zeidler FontCacheReference& cacheReference, bool needsWriteLock) 138f4f30311SClemens Zeidler { 139f4f30311SClemens Zeidler ASSERT(cacheReference.Entry() == NULL); 140f4f30311SClemens Zeidler 141f4f30311SClemens Zeidler FontCache* cache = FontCache::Default(); 142750958b8SStephan Aßmus FontCacheEntry* entry = cache->FontCacheEntryFor(font, forceVector); 143f4f30311SClemens Zeidler if (entry == NULL) 144f4f30311SClemens Zeidler return NULL; 145f4f30311SClemens Zeidler 146f4f30311SClemens Zeidler if (entry == disallowedEntry) { 147f4f30311SClemens Zeidler cache->Recycle(entry); 148f4f30311SClemens Zeidler return NULL; 149f4f30311SClemens Zeidler } 150f4f30311SClemens Zeidler 151f4f30311SClemens Zeidler if (needsWriteLock) { 152f4f30311SClemens Zeidler if (!entry->WriteLock()) { 153f4f30311SClemens Zeidler cache->Recycle(entry); 154f4f30311SClemens Zeidler return NULL; 155f4f30311SClemens Zeidler } 156f4f30311SClemens Zeidler } else { 157f4f30311SClemens Zeidler if (!entry->ReadLock()) { 158f4f30311SClemens Zeidler cache->Recycle(entry); 159f4f30311SClemens Zeidler return NULL; 160f4f30311SClemens Zeidler } 161f4f30311SClemens Zeidler } 162f4f30311SClemens Zeidler 163f4f30311SClemens Zeidler // At this point, we have a valid FontCacheEntry and it is locked in the 164f4f30311SClemens Zeidler // proper mode. We can setup the FontCacheReference so it takes care of 165f4f30311SClemens Zeidler // the locking and recycling from now and return the entry. 166f4f30311SClemens Zeidler cacheReference.SetTo(entry, needsWriteLock); 167f4f30311SClemens Zeidler return entry; 168f4f30311SClemens Zeidler } 169f4f30311SClemens Zeidler 170f4f30311SClemens Zeidler 171f4f30311SClemens Zeidler template<class GlyphConsumer> 172f4f30311SClemens Zeidler inline bool 173f4f30311SClemens Zeidler GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, 174f4f30311SClemens Zeidler const ServerFont& font, 175f4f30311SClemens Zeidler const char* utf8String, int32 length, 176f4f30311SClemens Zeidler const escapement_delta* delta, bool kerning, uint8 spacing, 177f4f30311SClemens Zeidler const BPoint* offsets, FontCacheReference* _cacheReference) 178f4f30311SClemens Zeidler { 179f4f30311SClemens Zeidler // TODO: implement spacing modes 180f4f30311SClemens Zeidler FontCacheEntry* entry = NULL; 181f4f30311SClemens Zeidler FontCacheReference cacheReference; 182f4f30311SClemens Zeidler FontCacheEntry* fallbackEntry = NULL; 183f4f30311SClemens Zeidler FontCacheReference fallbackCacheReference; 184f4f30311SClemens Zeidler if (_cacheReference != NULL) { 185f4f30311SClemens Zeidler entry = _cacheReference->Entry(); 186f4f30311SClemens Zeidler // When there is already a cacheReference, it means there was already 187f4f30311SClemens Zeidler // an iteration over the glyphs. The use-case is for example to do 188f4f30311SClemens Zeidler // a layout pass to get the string width for the bounding box, then a 189f4f30311SClemens Zeidler // second layout pass to actually render the glyphs to the screen. 190f4f30311SClemens Zeidler // This means that the fallback entry mechanism will not do any good 191f4f30311SClemens Zeidler // for the second pass, since the fallback glyphs have been stored in 192f4f30311SClemens Zeidler // the original entry. 193f4f30311SClemens Zeidler } 194f4f30311SClemens Zeidler 195f4f30311SClemens Zeidler if (entry == NULL) { 196750958b8SStephan Aßmus entry = FontCacheEntryFor(font, consumer.NeedsVector(), NULL, 197750958b8SStephan Aßmus utf8String, length, cacheReference, false); 198f4f30311SClemens Zeidler 199f4f30311SClemens Zeidler if (entry == NULL) 200f4f30311SClemens Zeidler return false; 201f4f30311SClemens Zeidler } // else the entry was already used and is still locked 202f4f30311SClemens Zeidler 203f4f30311SClemens Zeidler consumer.Start(); 204f4f30311SClemens Zeidler 205f4f30311SClemens Zeidler double x = 0.0; 206f4f30311SClemens Zeidler double y = 0.0; 207f4f30311SClemens Zeidler if (offsets) { 208f4f30311SClemens Zeidler x = offsets[0].x; 209f4f30311SClemens Zeidler y = offsets[0].y; 210f4f30311SClemens Zeidler } 211f4f30311SClemens Zeidler 212f4f30311SClemens Zeidler double advanceX = 0.0; 213f4f30311SClemens Zeidler double advanceY = 0.0; 214f4f30311SClemens Zeidler 215750958b8SStephan Aßmus // uint32 lastCharCode = 0; // Needed for kerning, see below 216f4f30311SClemens Zeidler uint32 charCode; 217f4f30311SClemens Zeidler int32 index = 0; 218f4f30311SClemens Zeidler bool writeLocked = false; 219f4f30311SClemens Zeidler const char* start = utf8String; 220f4f30311SClemens Zeidler while ((charCode = UTF8ToCharCode(&utf8String))) { 221f4f30311SClemens Zeidler 22224c156daSStephan Aßmus if (offsets != NULL) { 223f4f30311SClemens Zeidler // Use direct glyph locations instead of calculating them 224f4f30311SClemens Zeidler // from the advance values 225f4f30311SClemens Zeidler x = offsets[index].x; 226f4f30311SClemens Zeidler y = offsets[index].y; 227f4f30311SClemens Zeidler } else { 228f4f30311SClemens Zeidler // TODO: Currently disabled, because it works much too slow (doesn't seem 229f4f30311SClemens Zeidler // to be properly cached in FreeType.) 230f4f30311SClemens Zeidler // if (kerning) 231f4f30311SClemens Zeidler // entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY); 232f4f30311SClemens Zeidler 233f4f30311SClemens Zeidler x += advanceX; 234f4f30311SClemens Zeidler y += advanceY; 235f4f30311SClemens Zeidler } 236f4f30311SClemens Zeidler 237f4f30311SClemens Zeidler const GlyphCache* glyph = entry->CachedGlyph(charCode); 238f4f30311SClemens Zeidler if (glyph == NULL) { 239f4f30311SClemens Zeidler // The glyph has not been cached yet, switch to a write lock, 240f4f30311SClemens Zeidler // acquire the fallback entry and create the glyph. Note that 241f4f30311SClemens Zeidler // the write lock will persist (in the cacheReference) so that 242f4f30311SClemens Zeidler // we only have to do this switch once for the whole string. 243f4f30311SClemens Zeidler if (!writeLocked) { 244f4f30311SClemens Zeidler writeLocked = _WriteLockAndAcquireFallbackEntry(cacheReference, 245750958b8SStephan Aßmus entry, font, consumer.NeedsVector(), utf8String, length, 246750958b8SStephan Aßmus fallbackCacheReference, fallbackEntry); 247f4f30311SClemens Zeidler } 248f4f30311SClemens Zeidler 249f4f30311SClemens Zeidler if (writeLocked) 250f4f30311SClemens Zeidler glyph = entry->CreateGlyph(charCode, fallbackEntry); 251f4f30311SClemens Zeidler } 252f4f30311SClemens Zeidler 253f4f30311SClemens Zeidler if (glyph == NULL) { 254f4f30311SClemens Zeidler consumer.ConsumeEmptyGlyph(index++, charCode, x, y); 255f4f30311SClemens Zeidler advanceX = 0; 256f4f30311SClemens Zeidler advanceY = 0; 257f4f30311SClemens Zeidler } else { 258f4f30311SClemens Zeidler // get next increment for pen position 259f4f30311SClemens Zeidler advanceX = glyph->advance_x; 260f4f30311SClemens Zeidler advanceY = glyph->advance_y; 261*4ccc40a1SStephan Aßmus 262*4ccc40a1SStephan Aßmus // adjust for custom spacing 263*4ccc40a1SStephan Aßmus if (delta != NULL) { 264*4ccc40a1SStephan Aßmus advanceX += IsWhiteSpace(charCode) 265*4ccc40a1SStephan Aßmus ? delta->space : delta->nonspace; 266*4ccc40a1SStephan Aßmus } 267*4ccc40a1SStephan Aßmus 268*4ccc40a1SStephan Aßmus if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y, 269*4ccc40a1SStephan Aßmus advanceX, advanceY)) { 270*4ccc40a1SStephan Aßmus advanceX = 0.0; 271*4ccc40a1SStephan Aßmus advanceY = 0.0; 272*4ccc40a1SStephan Aßmus break; 273*4ccc40a1SStephan Aßmus } 274f4f30311SClemens Zeidler } 275f4f30311SClemens Zeidler 2765c6b9eb0SJerome Duval // lastCharCode = charCode; 277f4f30311SClemens Zeidler if (utf8String - start + 1 > length) 278f4f30311SClemens Zeidler break; 279f4f30311SClemens Zeidler } 280f4f30311SClemens Zeidler 281f4f30311SClemens Zeidler x += advanceX; 282f4f30311SClemens Zeidler y += advanceY; 283f4f30311SClemens Zeidler consumer.Finish(x, y); 284f4f30311SClemens Zeidler 285f4f30311SClemens Zeidler if (_cacheReference != NULL && _cacheReference->Entry() == NULL) { 286f4f30311SClemens Zeidler // The caller passed a FontCacheReference, but this is the first 287f4f30311SClemens Zeidler // iteration -> switch the ownership from the stack allocated 288f4f30311SClemens Zeidler // FontCacheReference to the one passed by the caller. The fallback 289f4f30311SClemens Zeidler // FontCacheReference is not affected by this, since it is never used 290f4f30311SClemens Zeidler // during a second iteration. 291f4f30311SClemens Zeidler _cacheReference->SetTo(entry, cacheReference.WriteLocked()); 292f4f30311SClemens Zeidler cacheReference.SetTo(NULL, false); 293f4f30311SClemens Zeidler } 294f4f30311SClemens Zeidler return true; 295f4f30311SClemens Zeidler } 296f4f30311SClemens Zeidler 297f4f30311SClemens Zeidler 298f4f30311SClemens Zeidler inline bool 299f4f30311SClemens Zeidler GlyphLayoutEngine::_WriteLockAndAcquireFallbackEntry( 300f4f30311SClemens Zeidler FontCacheReference& cacheReference, FontCacheEntry* entry, 301750958b8SStephan Aßmus const ServerFont& font, bool forceVector, const char* utf8String, 302750958b8SStephan Aßmus int32 length, FontCacheReference& fallbackCacheReference, 303750958b8SStephan Aßmus FontCacheEntry*& fallbackEntry) 304f4f30311SClemens Zeidler { 305f4f30311SClemens Zeidler // We need the fallback font, since potentially, we have to obtain missing 306f4f30311SClemens Zeidler // glyphs from it. We need to obtain the fallback font while we have not 307f4f30311SClemens Zeidler // locked anything, since locking the FontManager with the write-lock held 308f4f30311SClemens Zeidler // can obvisouly lead to a deadlock. 309f4f30311SClemens Zeidler 310f4f30311SClemens Zeidler cacheReference.SetTo(NULL, false); 311f4f30311SClemens Zeidler entry->ReadUnlock(); 312f4f30311SClemens Zeidler 313f4f30311SClemens Zeidler if (gFontManager->Lock()) { 314f4f30311SClemens Zeidler // TODO: We always get the fallback glyphs from VL Gothic at the 315f4f30311SClemens Zeidler // moment, but of course the fallback font should a) contain the 316f4f30311SClemens Zeidler // missing glyphs at all and b) be similar to the original font. 317f4f30311SClemens Zeidler // So there should be a mapping of some kind to know the most 318f4f30311SClemens Zeidler // suitable fallback font. 319f4f30311SClemens Zeidler FontStyle* fallbackStyle = gFontManager->GetStyleByIndex( 320f4f30311SClemens Zeidler "VL Gothic", 0); 321f4f30311SClemens Zeidler if (fallbackStyle != NULL) { 322f4f30311SClemens Zeidler ServerFont fallbackFont(*fallbackStyle, font.Size()); 323f4f30311SClemens Zeidler gFontManager->Unlock(); 324f4f30311SClemens Zeidler // Force the write-lock on the fallback entry, since we 325f4f30311SClemens Zeidler // don't transfer or copy GlyphCache objects from one cache 326f4f30311SClemens Zeidler // to the other, but create new glyphs which are stored in 327f4f30311SClemens Zeidler // "entry" in any case, which requires the write cache for 328f4f30311SClemens Zeidler // sure (used FontEngine of fallbackEntry). 329750958b8SStephan Aßmus fallbackEntry = FontCacheEntryFor(fallbackFont, forceVector, entry, 330f4f30311SClemens Zeidler utf8String, length, fallbackCacheReference, true); 331f4f30311SClemens Zeidler // NOTE: We don't care if fallbackEntry is NULL, fetching 332f4f30311SClemens Zeidler // alternate glyphs will simply not work. 333f4f30311SClemens Zeidler } else 334f4f30311SClemens Zeidler gFontManager->Unlock(); 335f4f30311SClemens Zeidler } 336f4f30311SClemens Zeidler 337f4f30311SClemens Zeidler if (!entry->WriteLock()) { 338f4f30311SClemens Zeidler FontCache::Default()->Recycle(entry); 339f4f30311SClemens Zeidler return false; 340f4f30311SClemens Zeidler } 341f4f30311SClemens Zeidler 342f4f30311SClemens Zeidler // Update the FontCacheReference, since the locking kind changed. 343f4f30311SClemens Zeidler cacheReference.SetTo(entry, true); 344f4f30311SClemens Zeidler return true; 345f4f30311SClemens Zeidler } 346f4f30311SClemens Zeidler 347f4f30311SClemens Zeidler 348f4f30311SClemens Zeidler #endif // GLYPH_LAYOUT_ENGINE_H 349