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 #include <ObjectList.h> 22 #include <SupportDefs.h> 23 24 #include <ctype.h> 25 26 class FontCacheReference { 27 public: 28 FontCacheReference() 29 : 30 fCacheEntry(NULL), 31 fWriteLocked(false) 32 { 33 } 34 35 ~FontCacheReference() 36 { 37 Unset(); 38 } 39 40 bool SetTo(FontCacheEntry* entry, bool writeLock) 41 { 42 ASSERT(entry != NULL); 43 44 if (entry == fCacheEntry) { 45 if (writeLock == fWriteLocked) 46 return true; 47 UnlockAndDisown(); 48 } else if (fCacheEntry != NULL) 49 Unset(); 50 51 if (writeLock) { 52 if (!entry->WriteLock()) { 53 FontCache::Default()->Recycle(entry); 54 return false; 55 } 56 } else if (!entry->ReadLock()) { 57 FontCache::Default()->Recycle(entry); 58 return false; 59 } 60 61 fCacheEntry = entry; 62 fWriteLocked = writeLock; 63 return true; 64 } 65 66 FontCacheEntry* UnlockAndDisown() 67 { 68 if (fCacheEntry == NULL) 69 return NULL; 70 71 if (fWriteLocked) 72 fCacheEntry->WriteUnlock(); 73 else 74 fCacheEntry->ReadUnlock(); 75 76 FontCacheEntry* entry = fCacheEntry; 77 fCacheEntry = NULL; 78 fWriteLocked = false; 79 return entry; 80 } 81 82 void Unset() 83 { 84 if (fCacheEntry == NULL) 85 return; 86 87 FontCache::Default()->Recycle(UnlockAndDisown()); 88 } 89 90 inline FontCacheEntry* Entry() const 91 { 92 return fCacheEntry; 93 } 94 95 inline bool WriteLocked() const 96 { 97 return fWriteLocked; 98 } 99 100 private: 101 FontCacheEntry* fCacheEntry; 102 bool fWriteLocked; 103 }; 104 105 106 class GlyphLayoutEngine { 107 public: 108 static bool IsWhiteSpace(uint32 glyphCode); 109 110 static FontCacheEntry* FontCacheEntryFor(const ServerFont& font, 111 bool forceVector); 112 113 template<class GlyphConsumer> 114 static bool LayoutGlyphs(GlyphConsumer& consumer, 115 const ServerFont& font, 116 const char* utf8String, 117 int32 length, int32 maxChars, 118 const escapement_delta* delta = NULL, 119 uint8 spacing = B_BITMAP_SPACING, 120 const BPoint* offsets = NULL, 121 FontCacheReference* cacheReference = NULL); 122 123 static void PopulateAndLockFallbacks( 124 BObjectList<FontCacheReference>& fallbacks, 125 const ServerFont& font, bool forceVector, 126 bool writeLock); 127 128 private: 129 static const GlyphCache* _CreateGlyph( 130 FontCacheReference& cacheReference, 131 BObjectList<FontCacheReference>& fallbacks, 132 const ServerFont& font, bool needsVector, 133 uint32 glyphCode); 134 135 GlyphLayoutEngine(); 136 virtual ~GlyphLayoutEngine(); 137 }; 138 139 140 inline bool 141 GlyphLayoutEngine::IsWhiteSpace(uint32 charCode) 142 { 143 switch (charCode) { 144 case 0x0009: /* tab */ 145 case 0x000b: /* vertical tab */ 146 case 0x000c: /* form feed */ 147 case 0x0020: /* space */ 148 case 0x00a0: /* non breaking space */ 149 case 0x000a: /* line feed */ 150 case 0x000d: /* carriage return */ 151 case 0x2028: /* line separator */ 152 case 0x2029: /* paragraph separator */ 153 return true; 154 } 155 156 return false; 157 } 158 159 160 inline FontCacheEntry* 161 GlyphLayoutEngine::FontCacheEntryFor(const ServerFont& font, bool forceVector) 162 { 163 FontCache* cache = FontCache::Default(); 164 FontCacheEntry* entry = cache->FontCacheEntryFor(font, forceVector); 165 return entry; 166 } 167 168 169 template<class GlyphConsumer> 170 inline bool 171 GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, 172 const ServerFont& font, 173 const char* utf8String, int32 length, int32 maxChars, 174 const escapement_delta* delta, uint8 spacing, 175 const BPoint* offsets, FontCacheReference* _cacheReference) 176 { 177 // TODO: implement spacing modes 178 FontCacheEntry* entry = NULL; 179 FontCacheReference* pCacheReference; 180 FontCacheReference cacheReference; 181 BObjectList<FontCacheReference> fallbacksList(21, true); 182 183 if (_cacheReference != NULL) { 184 pCacheReference = _cacheReference; 185 entry = _cacheReference->Entry(); 186 // When there is already a cacheReference, it means there was already 187 // an iteration over the glyphs. The use-case is for example to do 188 // a layout pass to get the string width for the bounding box, then a 189 // second layout pass to actually render the glyphs to the screen. 190 // This means that the fallback entry mechanism will not do any good 191 // for the second pass, since the fallback glyphs have been stored in 192 // the original entry. 193 } else 194 pCacheReference = &cacheReference; 195 196 if (entry == NULL) { 197 entry = FontCacheEntryFor(font, consumer.NeedsVector()); 198 199 if (entry == NULL) 200 return false; 201 if (!pCacheReference->SetTo(entry, false)) 202 return false; 203 } // else the entry was already used and is still locked 204 205 consumer.Start(); 206 207 double x = 0.0; 208 double y = 0.0; 209 if (offsets) { 210 x = offsets[0].x; 211 y = offsets[0].y; 212 } 213 214 double advanceX = 0.0; 215 double advanceY = 0.0; 216 double size = font.Size(); 217 218 uint32 lastCharCode = 0; // Needed for kerning in B_STRING_SPACING mode 219 uint32 charCode; 220 int32 index = 0; 221 const char* start = utf8String; 222 while (maxChars-- > 0 && (charCode = UTF8ToCharCode(&utf8String)) != 0) { 223 224 if (offsets != NULL) { 225 // Use direct glyph locations instead of calculating them 226 // from the advance values 227 x = offsets[index].x; 228 y = offsets[index].y; 229 } else { 230 if (spacing == B_STRING_SPACING) 231 entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY); 232 233 x += advanceX; 234 y += advanceY; 235 } 236 237 const GlyphCache* glyph = entry->CachedGlyph(charCode); 238 if (glyph == NULL) { 239 glyph = _CreateGlyph(*pCacheReference, fallbacksList, font, 240 consumer.NeedsVector(), charCode); 241 242 // Something may have gone wrong while reacquiring the entry lock 243 if (pCacheReference->Entry() == NULL) 244 return false; 245 } 246 247 if (glyph == NULL) { 248 consumer.ConsumeEmptyGlyph(index++, charCode, x, y); 249 advanceX = 0; 250 advanceY = 0; 251 } else { 252 // get next increment for pen position 253 if (spacing == B_CHAR_SPACING) { 254 advanceX = glyph->precise_advance_x * size; 255 advanceY = glyph->precise_advance_y * size; 256 } else { 257 advanceX = glyph->advance_x; 258 advanceY = glyph->advance_y; 259 } 260 261 // adjust for custom spacing 262 if (delta != NULL) { 263 advanceX += IsWhiteSpace(charCode) 264 ? delta->space : delta->nonspace; 265 } 266 267 if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y, 268 advanceX, advanceY)) { 269 advanceX = 0.0; 270 advanceY = 0.0; 271 break; 272 } 273 } 274 275 lastCharCode = charCode; 276 if (utf8String - start + 1 > length) 277 break; 278 } 279 280 x += advanceX; 281 y += advanceY; 282 consumer.Finish(x, y); 283 284 return true; 285 } 286 287 288 inline const GlyphCache* 289 GlyphLayoutEngine::_CreateGlyph(FontCacheReference& cacheReference, 290 BObjectList<FontCacheReference>& fallbacks, 291 const ServerFont& font, bool forceVector, uint32 charCode) 292 { 293 FontCacheEntry* entry = cacheReference.Entry(); 294 295 // Avoid loading and locking the fallbacks if our font can create the glyph. 296 if (entry->CanCreateGlyph(charCode)) { 297 if (cacheReference.SetTo(entry, true)) 298 return entry->CreateGlyph(charCode); 299 return NULL; 300 } 301 302 if (fallbacks.IsEmpty()) { 303 // We need to create new glyphs with the engine of the fallback font 304 // and store them in the main font cache (not just transfer them from 305 // one cache to the other). So we need both to be write-locked. 306 // The main font is unlocked first, in case it is also in the fallback 307 // list, so that we always keep the same order to avoid deadlocks. 308 cacheReference.UnlockAndDisown(); 309 PopulateAndLockFallbacks(fallbacks, font, forceVector, true); 310 if (!cacheReference.SetTo(entry, true)) { 311 return NULL; 312 } 313 } 314 315 int32 count = fallbacks.CountItems(); 316 for (int32 index = 0; index < count; index++) { 317 FontCacheEntry* fallbackEntry = fallbacks.ItemAt(index)->Entry(); 318 if (fallbackEntry->CanCreateGlyph(charCode)) 319 return entry->CreateGlyph(charCode, fallbackEntry); 320 } 321 322 return entry->CreateGlyph(charCode); 323 // No one knows how to draw this, so use the missing glyph symbol. 324 } 325 326 327 inline void 328 GlyphLayoutEngine::PopulateAndLockFallbacks( 329 BObjectList<FontCacheReference>& fallbacksList, 330 const ServerFont& font, bool forceVector, bool writeLock) 331 { 332 ASSERT(fallbacksList.IsEmpty()); 333 334 // TODO: We always get the fallback glyphs from the Noto family, but of 335 // course the fallback font should a) contain the missing glyphs at all 336 // and b) be similar to the original font. So there should be a mapping 337 // of some kind to know the most suitable fallback font. 338 static const char* fallbacks[] = { 339 "Noto Sans Display", 340 "Noto Sans Thai", 341 "Noto Sans CJK JP", 342 "Noto Sans Symbols", 343 "Noto Sans Symbols2", 344 "Noto Emoji", 345 }; 346 347 if (!gFontManager->Lock()) 348 return; 349 350 static const int nStyles = 3; 351 static const int nFallbacks = B_COUNT_OF(fallbacks); 352 FontCacheEntry* fallbackCacheEntries[nStyles * nFallbacks]; 353 int entries = 0; 354 355 for (int c = 0; c < nStyles; c++) { 356 const char* fontStyle; 357 if (c == 0) 358 fontStyle = font.Style(); 359 else if (c == 1) 360 fontStyle = "Regular"; 361 else 362 fontStyle = NULL; 363 364 for (int i = 0; i < nFallbacks; i++) { 365 366 FontStyle* fallbackStyle = gFontManager->GetStyle(fallbacks[i], 367 fontStyle, 0xffff, 0); 368 369 if (fallbackStyle == NULL) 370 continue; 371 372 ServerFont fallbackFont(*fallbackStyle, font.Size()); 373 374 FontCacheEntry* entry = FontCacheEntryFor( 375 fallbackFont, forceVector); 376 377 if (entry == NULL) 378 continue; 379 380 fallbackCacheEntries[entries++] = entry; 381 382 } 383 384 } 385 386 gFontManager->Unlock(); 387 388 // Finally lock the entries and save their references 389 for (int i = 0; i < entries; i++) { 390 FontCacheReference* cacheReference = 391 new(std::nothrow) FontCacheReference(); 392 if (cacheReference != NULL) { 393 if (cacheReference->SetTo(fallbackCacheEntries[i], writeLock)) 394 fallbacksList.AddItem(cacheReference); 395 else 396 delete cacheReference; 397 } 398 } 399 400 return; 401 } 402 403 404 #endif // GLYPH_LAYOUT_ENGINE_H 405