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