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