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 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 bool kerning = true, 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 const char* utf8String, int32 length, 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, const char* utf8String, int32 length, 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 (needsWriteLock) { 152 if (!entry->WriteLock()) { 153 cache->Recycle(entry); 154 return NULL; 155 } 156 } else { 157 if (!entry->ReadLock()) { 158 cache->Recycle(entry); 159 return NULL; 160 } 161 } 162 163 // At this point, we have a valid FontCacheEntry and it is locked in the 164 // proper mode. We can setup the FontCacheReference so it takes care of 165 // the locking and recycling from now and return the entry. 166 cacheReference.SetTo(entry, needsWriteLock); 167 return entry; 168 } 169 170 171 template<class GlyphConsumer> 172 inline bool 173 GlyphLayoutEngine::LayoutGlyphs(GlyphConsumer& consumer, 174 const ServerFont& font, 175 const char* utf8String, int32 length, 176 const escapement_delta* delta, bool kerning, uint8 spacing, 177 const BPoint* offsets, FontCacheReference* _cacheReference) 178 { 179 // TODO: implement spacing modes 180 FontCacheEntry* entry = NULL; 181 FontCacheReference cacheReference; 182 FontCacheEntry* fallbackEntry = NULL; 183 FontCacheReference fallbackCacheReference; 184 if (_cacheReference != NULL) { 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 } 194 195 if (entry == NULL) { 196 entry = FontCacheEntryFor(font, consumer.NeedsVector(), NULL, 197 utf8String, length, cacheReference, false); 198 199 if (entry == NULL) 200 return false; 201 } // else the entry was already used and is still locked 202 203 consumer.Start(); 204 205 double x = 0.0; 206 double y = 0.0; 207 if (offsets) { 208 x = offsets[0].x; 209 y = offsets[0].y; 210 } 211 212 double advanceX = 0.0; 213 double advanceY = 0.0; 214 215 // uint32 lastCharCode = 0; // Needed for kerning, see below 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 // TODO: Currently disabled, because it works much too slow (doesn't seem 229 // to be properly cached in FreeType.) 230 // if (kerning) 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 // The glyph has not been cached yet, switch to a write lock, 240 // acquire the fallback entry and create the glyph. Note that 241 // the write lock will persist (in the cacheReference) so that 242 // we only have to do this switch once for the whole string. 243 if (!writeLocked) { 244 writeLocked = _WriteLockAndAcquireFallbackEntry(cacheReference, 245 entry, font, consumer.NeedsVector(), utf8String, length, 246 fallbackCacheReference, fallbackEntry); 247 } 248 249 if (writeLocked) 250 glyph = entry->CreateGlyph(charCode, fallbackEntry); 251 } 252 253 if (glyph == NULL) { 254 consumer.ConsumeEmptyGlyph(index++, charCode, x, y); 255 advanceX = 0; 256 advanceY = 0; 257 } else { 258 // get next increment for pen position 259 advanceX = glyph->advance_x; 260 advanceY = glyph->advance_y; 261 262 // adjust for custom spacing 263 if (delta != NULL) { 264 advanceX += IsWhiteSpace(charCode) 265 ? delta->space : delta->nonspace; 266 } 267 268 if (!consumer.ConsumeGlyph(index++, charCode, glyph, entry, x, y, 269 advanceX, advanceY)) { 270 advanceX = 0.0; 271 advanceY = 0.0; 272 break; 273 } 274 } 275 276 // lastCharCode = charCode; 277 if (utf8String - start + 1 > length) 278 break; 279 } 280 281 x += advanceX; 282 y += advanceY; 283 consumer.Finish(x, y); 284 285 if (_cacheReference != NULL && _cacheReference->Entry() == NULL) { 286 // The caller passed a FontCacheReference, but this is the first 287 // iteration -> switch the ownership from the stack allocated 288 // FontCacheReference to the one passed by the caller. The fallback 289 // FontCacheReference is not affected by this, since it is never used 290 // during a second iteration. 291 _cacheReference->SetTo(entry, cacheReference.WriteLocked()); 292 cacheReference.SetTo(NULL, false); 293 } 294 return true; 295 } 296 297 298 inline bool 299 GlyphLayoutEngine::_WriteLockAndAcquireFallbackEntry( 300 FontCacheReference& cacheReference, FontCacheEntry* entry, 301 const ServerFont& font, bool forceVector, const char* utf8String, 302 int32 length, FontCacheReference& fallbackCacheReference, 303 FontCacheEntry*& fallbackEntry) 304 { 305 // We need the fallback font, since potentially, we have to obtain missing 306 // glyphs from it. We need to obtain the fallback font while we have not 307 // locked anything, since locking the FontManager with the write-lock held 308 // can obvisouly lead to a deadlock. 309 310 cacheReference.SetTo(NULL, false); 311 entry->ReadUnlock(); 312 313 if (gFontManager->Lock()) { 314 // TODO: We always get the fallback glyphs from VL Gothic at the 315 // moment, but of course the fallback font should a) contain the 316 // missing glyphs at all and b) be similar to the original font. 317 // So there should be a mapping of some kind to know the most 318 // suitable fallback font. 319 FontStyle* fallbackStyle = gFontManager->GetStyleByIndex( 320 "VL Gothic", 0); 321 if (fallbackStyle != NULL) { 322 ServerFont fallbackFont(*fallbackStyle, font.Size()); 323 gFontManager->Unlock(); 324 // Force the write-lock on the fallback entry, since we 325 // don't transfer or copy GlyphCache objects from one cache 326 // to the other, but create new glyphs which are stored in 327 // "entry" in any case, which requires the write cache for 328 // sure (used FontEngine of fallbackEntry). 329 fallbackEntry = FontCacheEntryFor(fallbackFont, forceVector, entry, 330 utf8String, length, fallbackCacheReference, true); 331 // NOTE: We don't care if fallbackEntry is NULL, fetching 332 // alternate glyphs will simply not work. 333 } else 334 gFontManager->Unlock(); 335 } 336 337 if (!entry->WriteLock()) { 338 FontCache::Default()->Recycle(entry); 339 return false; 340 } 341 342 // Update the FontCacheReference, since the locking kind changed. 343 cacheReference.SetTo(entry, true); 344 return true; 345 } 346 347 348 #endif // GLYPH_LAYOUT_ENGINE_H 349