1f4f30311SClemens Zeidler /* 291233f88SStephan Aßmus * Copyright 2007-2014, 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 uint8 spacing = B_BITMAP_SPACING, 96f4f30311SClemens Zeidler const BPoint* offsets = NULL, 97f4f30311SClemens Zeidler FontCacheReference* cacheReference = NULL); 98f4f30311SClemens Zeidler 99f4f30311SClemens Zeidler private: 100f4f30311SClemens Zeidler static bool _WriteLockAndAcquireFallbackEntry( 101f4f30311SClemens Zeidler FontCacheReference& cacheReference, 102f4f30311SClemens Zeidler FontCacheEntry* entry, 103750958b8SStephan Aßmus const ServerFont& font, bool needsVector, 104f4f30311SClemens Zeidler const char* utf8String, int32 length, 105f4f30311SClemens Zeidler FontCacheReference& fallbackCacheReference, 106f4f30311SClemens Zeidler FontCacheEntry*& fallbackEntry); 107f4f30311SClemens Zeidler 108f4f30311SClemens Zeidler GlyphLayoutEngine(); 109f4f30311SClemens Zeidler virtual ~GlyphLayoutEngine(); 110f4f30311SClemens Zeidler }; 111f4f30311SClemens Zeidler 112f4f30311SClemens Zeidler 113f4f30311SClemens Zeidler inline bool 114f4f30311SClemens Zeidler GlyphLayoutEngine::IsWhiteSpace(uint32 charCode) 115f4f30311SClemens Zeidler { 116f4f30311SClemens Zeidler switch (charCode) { 117f4f30311SClemens Zeidler case 0x0009: /* tab */ 118f4f30311SClemens Zeidler case 0x000b: /* vertical tab */ 119f4f30311SClemens Zeidler case 0x000c: /* form feed */ 120f4f30311SClemens Zeidler case 0x0020: /* space */ 121f4f30311SClemens Zeidler case 0x00a0: /* non breaking space */ 122f4f30311SClemens Zeidler case 0x000a: /* line feed */ 123f4f30311SClemens Zeidler case 0x000d: /* carriage return */ 124f4f30311SClemens Zeidler case 0x2028: /* line separator */ 125f4f30311SClemens Zeidler case 0x2029: /* paragraph separator */ 126f4f30311SClemens Zeidler return true; 127f4f30311SClemens Zeidler } 128f4f30311SClemens Zeidler 129f4f30311SClemens Zeidler return false; 130f4f30311SClemens Zeidler } 131f4f30311SClemens Zeidler 132f4f30311SClemens Zeidler 133f4f30311SClemens Zeidler inline FontCacheEntry* 134750958b8SStephan Aßmus GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font, bool forceVector, 135f4f30311SClemens Zeidler const FontCacheEntry* disallowedEntry, const char* utf8String, int32 length, 136f4f30311SClemens Zeidler FontCacheReference& cacheReference, bool needsWriteLock) 137f4f30311SClemens Zeidler { 138f4f30311SClemens Zeidler ASSERT(cacheReference.Entry() == NULL); 139f4f30311SClemens Zeidler 140f4f30311SClemens Zeidler FontCache* cache = FontCache::Default(); 141750958b8SStephan Aßmus FontCacheEntry* entry = cache->FontCacheEntryFor(font, forceVector); 142f4f30311SClemens Zeidler if (entry == NULL) 143f4f30311SClemens Zeidler return NULL; 144f4f30311SClemens Zeidler 145f4f30311SClemens Zeidler if (entry == disallowedEntry) { 146f4f30311SClemens Zeidler cache->Recycle(entry); 147f4f30311SClemens Zeidler return NULL; 148f4f30311SClemens Zeidler } 149f4f30311SClemens Zeidler 150f4f30311SClemens Zeidler if (needsWriteLock) { 151f4f30311SClemens Zeidler if (!entry->WriteLock()) { 152f4f30311SClemens Zeidler cache->Recycle(entry); 153f4f30311SClemens Zeidler return NULL; 154f4f30311SClemens Zeidler } 155f4f30311SClemens Zeidler } else { 156f4f30311SClemens Zeidler if (!entry->ReadLock()) { 157f4f30311SClemens Zeidler cache->Recycle(entry); 158f4f30311SClemens Zeidler return NULL; 159f4f30311SClemens Zeidler } 160f4f30311SClemens Zeidler } 161f4f30311SClemens Zeidler 162f4f30311SClemens Zeidler // At this point, we have a valid FontCacheEntry and it is locked in the 163f4f30311SClemens Zeidler // proper mode. We can setup the FontCacheReference so it takes care of 164f4f30311SClemens Zeidler // the locking and recycling from now and return the entry. 165f4f30311SClemens Zeidler cacheReference.SetTo(entry, needsWriteLock); 166f4f30311SClemens Zeidler return entry; 167f4f30311SClemens Zeidler } 168f4f30311SClemens Zeidler 169f4f30311SClemens Zeidler 170f4f30311SClemens Zeidler template<class GlyphConsumer> 171f4f30311SClemens Zeidler inline bool 172f4f30311SClemens Zeidler GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, 173f4f30311SClemens Zeidler const ServerFont& font, 174f4f30311SClemens Zeidler const char* utf8String, int32 length, 17591233f88SStephan Aßmus const escapement_delta* delta, uint8 spacing, 176f4f30311SClemens Zeidler const BPoint* offsets, FontCacheReference* _cacheReference) 177f4f30311SClemens Zeidler { 178f4f30311SClemens Zeidler // TODO: implement spacing modes 179f4f30311SClemens Zeidler FontCacheEntry* entry = NULL; 180f4f30311SClemens Zeidler FontCacheReference cacheReference; 181f4f30311SClemens Zeidler FontCacheEntry* fallbackEntry = NULL; 182f4f30311SClemens Zeidler FontCacheReference fallbackCacheReference; 183f4f30311SClemens Zeidler if (_cacheReference != NULL) { 184f4f30311SClemens Zeidler entry = _cacheReference->Entry(); 185f4f30311SClemens Zeidler // When there is already a cacheReference, it means there was already 186f4f30311SClemens Zeidler // an iteration over the glyphs. The use-case is for example to do 187f4f30311SClemens Zeidler // a layout pass to get the string width for the bounding box, then a 188f4f30311SClemens Zeidler // second layout pass to actually render the glyphs to the screen. 189f4f30311SClemens Zeidler // This means that the fallback entry mechanism will not do any good 190f4f30311SClemens Zeidler // for the second pass, since the fallback glyphs have been stored in 191f4f30311SClemens Zeidler // the original entry. 192f4f30311SClemens Zeidler } 193f4f30311SClemens Zeidler 194f4f30311SClemens Zeidler if (entry == NULL) { 195750958b8SStephan Aßmus entry = FontCacheEntryFor(font, consumer.NeedsVector(), NULL, 196750958b8SStephan Aßmus utf8String, length, cacheReference, false); 197f4f30311SClemens Zeidler 198f4f30311SClemens Zeidler if (entry == NULL) 199f4f30311SClemens Zeidler return false; 200f4f30311SClemens Zeidler } // else the entry was already used and is still locked 201f4f30311SClemens Zeidler 202f4f30311SClemens Zeidler consumer.Start(); 203f4f30311SClemens Zeidler 204f4f30311SClemens Zeidler double x = 0.0; 205f4f30311SClemens Zeidler double y = 0.0; 206f4f30311SClemens Zeidler if (offsets) { 207f4f30311SClemens Zeidler x = offsets[0].x; 208f4f30311SClemens Zeidler y = offsets[0].y; 209f4f30311SClemens Zeidler } 210f4f30311SClemens Zeidler 211f4f30311SClemens Zeidler double advanceX = 0.0; 212f4f30311SClemens Zeidler double advanceY = 0.0; 213*b8f4968dSStephan Aßmus double size = font.Size(); 214f4f30311SClemens Zeidler 21591233f88SStephan Aßmus uint32 lastCharCode = 0; // Needed for kerning in B_STRING_SPACING mode 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 { 22891233f88SStephan Aßmus if (spacing == B_STRING_SPACING) 22991233f88SStephan Aßmus entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY); 230f4f30311SClemens Zeidler 231f4f30311SClemens Zeidler x += advanceX; 232f4f30311SClemens Zeidler y += advanceY; 233f4f30311SClemens Zeidler } 234f4f30311SClemens Zeidler 235f4f30311SClemens Zeidler const GlyphCache* glyph = entry->CachedGlyph(charCode); 236f4f30311SClemens Zeidler if (glyph == NULL) { 237f4f30311SClemens Zeidler // The glyph has not been cached yet, switch to a write lock, 238f4f30311SClemens Zeidler // acquire the fallback entry and create the glyph. Note that 239f4f30311SClemens Zeidler // the write lock will persist (in the cacheReference) so that 240f4f30311SClemens Zeidler // we only have to do this switch once for the whole string. 241f4f30311SClemens Zeidler if (!writeLocked) { 242f4f30311SClemens Zeidler writeLocked = _WriteLockAndAcquireFallbackEntry(cacheReference, 243750958b8SStephan Aßmus entry, font, consumer.NeedsVector(), utf8String, length, 244750958b8SStephan Aßmus fallbackCacheReference, fallbackEntry); 245f4f30311SClemens Zeidler } 246f4f30311SClemens Zeidler 247f4f30311SClemens Zeidler if (writeLocked) 248f4f30311SClemens Zeidler glyph = entry->CreateGlyph(charCode, fallbackEntry); 249f4f30311SClemens Zeidler } 250f4f30311SClemens Zeidler 251f4f30311SClemens Zeidler if (glyph == NULL) { 252f4f30311SClemens Zeidler consumer.ConsumeEmptyGlyph(index++, charCode, x, y); 253f4f30311SClemens Zeidler advanceX = 0; 254f4f30311SClemens Zeidler advanceY = 0; 255f4f30311SClemens Zeidler } else { 256f4f30311SClemens Zeidler // get next increment for pen position 257d9d14326SStephan Aßmus if (spacing == B_CHAR_SPACING) { 258*b8f4968dSStephan Aßmus advanceX = glyph->precise_advance_x * size; 259*b8f4968dSStephan Aßmus advanceY = glyph->precise_advance_y * size; 260d9d14326SStephan Aßmus } else { 261f4f30311SClemens Zeidler advanceX = glyph->advance_x; 262f4f30311SClemens Zeidler advanceY = glyph->advance_y; 263d9d14326SStephan Aßmus } 2644ccc40a1SStephan Aßmus 2654ccc40a1SStephan Aßmus // adjust for custom spacing 2664ccc40a1SStephan Aßmus if (delta != NULL) { 2674ccc40a1SStephan Aßmus advanceX += IsWhiteSpace(charCode) 2684ccc40a1SStephan Aßmus ? delta->space : delta->nonspace; 2694ccc40a1SStephan Aßmus } 2704ccc40a1SStephan Aßmus 2714ccc40a1SStephan Aßmus if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y, 2724ccc40a1SStephan Aßmus advanceX, advanceY)) { 2734ccc40a1SStephan Aßmus advanceX = 0.0; 2744ccc40a1SStephan Aßmus advanceY = 0.0; 2754ccc40a1SStephan Aßmus break; 2764ccc40a1SStephan Aßmus } 277f4f30311SClemens Zeidler } 278f4f30311SClemens Zeidler 27991233f88SStephan Aßmus lastCharCode = charCode; 280f4f30311SClemens Zeidler if (utf8String - start + 1 > length) 281f4f30311SClemens Zeidler break; 282f4f30311SClemens Zeidler } 283f4f30311SClemens Zeidler 284f4f30311SClemens Zeidler x += advanceX; 285f4f30311SClemens Zeidler y += advanceY; 286f4f30311SClemens Zeidler consumer.Finish(x, y); 287f4f30311SClemens Zeidler 288f4f30311SClemens Zeidler if (_cacheReference != NULL && _cacheReference->Entry() == NULL) { 289f4f30311SClemens Zeidler // The caller passed a FontCacheReference, but this is the first 290f4f30311SClemens Zeidler // iteration -> switch the ownership from the stack allocated 291f4f30311SClemens Zeidler // FontCacheReference to the one passed by the caller. The fallback 292f4f30311SClemens Zeidler // FontCacheReference is not affected by this, since it is never used 293f4f30311SClemens Zeidler // during a second iteration. 294f4f30311SClemens Zeidler _cacheReference->SetTo(entry, cacheReference.WriteLocked()); 295f4f30311SClemens Zeidler cacheReference.SetTo(NULL, false); 296f4f30311SClemens Zeidler } 297f4f30311SClemens Zeidler return true; 298f4f30311SClemens Zeidler } 299f4f30311SClemens Zeidler 300f4f30311SClemens Zeidler 301f4f30311SClemens Zeidler inline bool 302f4f30311SClemens Zeidler GlyphLayoutEngine::_WriteLockAndAcquireFallbackEntry( 303f4f30311SClemens Zeidler FontCacheReference& cacheReference, FontCacheEntry* entry, 304750958b8SStephan Aßmus const ServerFont& font, bool forceVector, const char* utf8String, 305750958b8SStephan Aßmus int32 length, FontCacheReference& fallbackCacheReference, 306750958b8SStephan Aßmus FontCacheEntry*& fallbackEntry) 307f4f30311SClemens Zeidler { 308f4f30311SClemens Zeidler // We need the fallback font, since potentially, we have to obtain missing 309f4f30311SClemens Zeidler // glyphs from it. We need to obtain the fallback font while we have not 310f4f30311SClemens Zeidler // locked anything, since locking the FontManager with the write-lock held 311f4f30311SClemens Zeidler // can obvisouly lead to a deadlock. 312f4f30311SClemens Zeidler 313f4f30311SClemens Zeidler cacheReference.SetTo(NULL, false); 314f4f30311SClemens Zeidler entry->ReadUnlock(); 315f4f30311SClemens Zeidler 316f4f30311SClemens Zeidler if (gFontManager->Lock()) { 317f4f30311SClemens Zeidler // TODO: We always get the fallback glyphs from VL Gothic at the 318f4f30311SClemens Zeidler // moment, but of course the fallback font should a) contain the 319f4f30311SClemens Zeidler // missing glyphs at all and b) be similar to the original font. 320f4f30311SClemens Zeidler // So there should be a mapping of some kind to know the most 321f4f30311SClemens Zeidler // suitable fallback font. 322f4f30311SClemens Zeidler FontStyle* fallbackStyle = gFontManager->GetStyleByIndex( 323f4f30311SClemens Zeidler "VL Gothic", 0); 324f4f30311SClemens Zeidler if (fallbackStyle != NULL) { 325f4f30311SClemens Zeidler ServerFont fallbackFont(*fallbackStyle, font.Size()); 326f4f30311SClemens Zeidler gFontManager->Unlock(); 327f4f30311SClemens Zeidler // Force the write-lock on the fallback entry, since we 328f4f30311SClemens Zeidler // don't transfer or copy GlyphCache objects from one cache 329f4f30311SClemens Zeidler // to the other, but create new glyphs which are stored in 330f4f30311SClemens Zeidler // "entry" in any case, which requires the write cache for 331f4f30311SClemens Zeidler // sure (used FontEngine of fallbackEntry). 332750958b8SStephan Aßmus fallbackEntry = FontCacheEntryFor(fallbackFont, forceVector, entry, 333f4f30311SClemens Zeidler utf8String, length, fallbackCacheReference, true); 334f4f30311SClemens Zeidler // NOTE: We don't care if fallbackEntry is NULL, fetching 335f4f30311SClemens Zeidler // alternate glyphs will simply not work. 336f4f30311SClemens Zeidler } else 337f4f30311SClemens Zeidler gFontManager->Unlock(); 338f4f30311SClemens Zeidler } 339f4f30311SClemens Zeidler 340f4f30311SClemens Zeidler if (!entry->WriteLock()) { 341f4f30311SClemens Zeidler FontCache::Default()->Recycle(entry); 342f4f30311SClemens Zeidler return false; 343f4f30311SClemens Zeidler } 344f4f30311SClemens Zeidler 345f4f30311SClemens Zeidler // Update the FontCacheReference, since the locking kind changed. 346f4f30311SClemens Zeidler cacheReference.SetTo(entry, true); 347f4f30311SClemens Zeidler return true; 348f4f30311SClemens Zeidler } 349f4f30311SClemens Zeidler 350f4f30311SClemens Zeidler 351f4f30311SClemens Zeidler #endif // GLYPH_LAYOUT_ENGINE_H 352