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