1 /* 2 * Copyright 2007, 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 const FontCacheEntry* disallowedEntry, 84 const char* utf8String, int32 length, 85 FontCacheReference& cacheReference, 86 bool needsWriteLock); 87 88 template<class GlyphConsumer> 89 static bool LayoutGlyphs(GlyphConsumer& consumer, 90 const ServerFont& font, 91 const char* utf8String, 92 int32 length, 93 const escapement_delta* delta = NULL, 94 bool kerning = true, 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, 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, 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); 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, bool kerning, 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, NULL, utf8String, length, 196 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 214 // uint32 lastCharCode = 0; 215 uint32 charCode; 216 int32 index = 0; 217 bool writeLocked = false; 218 const char* start = utf8String; 219 while ((charCode = UTF8ToCharCode(&utf8String))) { 220 221 if (offsets) { 222 // Use direct glyph locations instead of calculating them 223 // from the advance values 224 x = offsets[index].x; 225 y = offsets[index].y; 226 } else { 227 // TODO: Currently disabled, because it works much too slow (doesn't seem 228 // to be properly cached in FreeType.) 229 // if (kerning) 230 // entry->GetKerning(lastCharCode, charCode, &advanceX, &advanceY); 231 232 x += advanceX; 233 y += advanceY; 234 235 if (delta) 236 x += IsWhiteSpace(charCode) ? delta->space : delta->nonspace; 237 } 238 239 const GlyphCache* glyph = entry->CachedGlyph(charCode); 240 if (glyph == NULL) { 241 // The glyph has not been cached yet, switch to a write lock, 242 // acquire the fallback entry and create the glyph. Note that 243 // the write lock will persist (in the cacheReference) so that 244 // we only have to do this switch once for the whole string. 245 if (!writeLocked) { 246 writeLocked = _WriteLockAndAcquireFallbackEntry(cacheReference, 247 entry, font, utf8String, length, fallbackCacheReference, 248 fallbackEntry); 249 } 250 251 if (writeLocked) 252 glyph = entry->CreateGlyph(charCode, fallbackEntry); 253 } 254 255 if (glyph == NULL) { 256 consumer.ConsumeEmptyGlyph(index++, charCode, x, y); 257 advanceX = 0; 258 advanceY = 0; 259 } else { 260 if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y)) { 261 advanceX = 0; 262 advanceY = 0; 263 break; 264 } 265 266 // get next increment for pen position 267 advanceX = glyph->advance_x; 268 advanceY = glyph->advance_y; 269 } 270 271 // lastCharCode = charCode; 272 if (utf8String - start + 1 > length) 273 break; 274 } 275 276 x += advanceX; 277 y += advanceY; 278 consumer.Finish(x, y); 279 280 if (_cacheReference != NULL && _cacheReference->Entry() == NULL) { 281 // The caller passed a FontCacheReference, but this is the first 282 // iteration -> switch the ownership from the stack allocated 283 // FontCacheReference to the one passed by the caller. The fallback 284 // FontCacheReference is not affected by this, since it is never used 285 // during a second iteration. 286 _cacheReference->SetTo(entry, cacheReference.WriteLocked()); 287 cacheReference.SetTo(NULL, false); 288 } 289 return true; 290 } 291 292 293 inline bool 294 GlyphLayoutEngine::_WriteLockAndAcquireFallbackEntry( 295 FontCacheReference& cacheReference, FontCacheEntry* entry, 296 const ServerFont& font, const char* utf8String, int32 length, 297 FontCacheReference& fallbackCacheReference, FontCacheEntry*& fallbackEntry) 298 { 299 // We need the fallback font, since potentially, we have to obtain missing 300 // glyphs from it. We need to obtain the fallback font while we have not 301 // locked anything, since locking the FontManager with the write-lock held 302 // can obvisouly lead to a deadlock. 303 304 cacheReference.SetTo(NULL, false); 305 entry->ReadUnlock(); 306 307 if (gFontManager->Lock()) { 308 // TODO: We always get the fallback glyphs from VL Gothic at the 309 // moment, but of course the fallback font should a) contain the 310 // missing glyphs at all and b) be similar to the original font. 311 // So there should be a mapping of some kind to know the most 312 // suitable fallback font. 313 FontStyle* fallbackStyle = gFontManager->GetStyleByIndex( 314 "VL Gothic", 0); 315 if (fallbackStyle != NULL) { 316 ServerFont fallbackFont(*fallbackStyle, font.Size()); 317 gFontManager->Unlock(); 318 // Force the write-lock on the fallback entry, since we 319 // don't transfer or copy GlyphCache objects from one cache 320 // to the other, but create new glyphs which are stored in 321 // "entry" in any case, which requires the write cache for 322 // sure (used FontEngine of fallbackEntry). 323 fallbackEntry = FontCacheEntryFor(fallbackFont, entry, 324 utf8String, length, fallbackCacheReference, true); 325 // NOTE: We don't care if fallbackEntry is NULL, fetching 326 // alternate glyphs will simply not work. 327 } else 328 gFontManager->Unlock(); 329 } 330 331 if (!entry->WriteLock()) { 332 FontCache::Default()->Recycle(entry); 333 return false; 334 } 335 336 // Update the FontCacheReference, since the locking kind changed. 337 cacheReference.SetTo(entry, true); 338 return true; 339 } 340 341 342 #endif // GLYPH_LAYOUT_ENGINE_H 343