1 /* 2 * Copyright 2007-2009, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Maxim Shemanarev <mcseemagg@yahoo.com> 7 * Stephan Aßmus <superstippi@gmx.de> 8 * Andrej Spielmann, <andrej.spielmann@seh.ox.ac.uk> 9 */ 10 11 //---------------------------------------------------------------------------- 12 // Anti-Grain Geometry - Version 2.4 13 // Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) 14 // 15 // Permission to copy, use, modify, sell and distribute this software 16 // is granted provided this copyright notice appears in all copies. 17 // This software is provided "as is" without express or implied 18 // warranty, and with no claim as to its suitability for any purpose. 19 // 20 //---------------------------------------------------------------------------- 21 // Contact: mcseem@antigrain.com 22 // mcseemagg@yahoo.com 23 // http://www.antigrain.com 24 //---------------------------------------------------------------------------- 25 26 27 #include "FontCacheEntry.h" 28 29 #include <string.h> 30 31 #include <new> 32 33 #include <Autolock.h> 34 35 #include <agg_array.h> 36 #include <utf8_functions.h> 37 #include <util/OpenHashTable.h> 38 39 #include "GlobalSubpixelSettings.h" 40 41 42 BLocker FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock"); 43 44 45 class FontCacheEntry::GlyphCachePool { 46 // This class needs to be defined before any inline functions, as otherwise 47 // gcc2 will barf in debug mode. 48 struct GlyphHashTableDefinition { 49 typedef uint32 KeyType; 50 typedef GlyphCache ValueType; 51 52 size_t HashKey(uint32 key) const 53 { 54 return key; 55 } 56 57 size_t Hash(GlyphCache* value) const 58 { 59 return value->glyph_index; 60 } 61 62 bool Compare(uint32 key, GlyphCache* value) const 63 { 64 return value->glyph_index == key; 65 } 66 67 GlyphCache*& GetLink(GlyphCache* value) const 68 { 69 return value->hash_link; 70 } 71 }; 72 public: 73 GlyphCachePool() 74 { 75 } 76 77 ~GlyphCachePool() 78 { 79 GlyphCache* glyph = fGlyphTable.Clear(true); 80 while (glyph != NULL) { 81 GlyphCache* next = glyph->hash_link; 82 delete glyph; 83 glyph = next; 84 } 85 } 86 87 status_t Init() 88 { 89 return fGlyphTable.Init(); 90 } 91 92 const GlyphCache* FindGlyph(uint32 glyphIndex) const 93 { 94 return fGlyphTable.Lookup(glyphIndex); 95 } 96 97 GlyphCache* CacheGlyph(uint32 glyphIndex, 98 uint32 dataSize, glyph_data_type dataType, const agg::rect_i& bounds, 99 float advanceX, float advanceY, float insetLeft, float insetRight) 100 { 101 GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex); 102 if (glyph != NULL) 103 return NULL; 104 105 glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType, 106 bounds, advanceX, advanceY, insetLeft, insetRight); 107 if (glyph == NULL || glyph->data == NULL) { 108 delete glyph; 109 return NULL; 110 } 111 112 // TODO: The HashTable grows without bounds. We should cleanup 113 // older entries from time to time. 114 115 fGlyphTable.Insert(glyph); 116 117 return glyph; 118 } 119 120 private: 121 typedef BOpenHashTable<GlyphHashTableDefinition> GlyphTable; 122 123 GlyphTable fGlyphTable; 124 }; 125 126 127 // #pragma mark - 128 129 130 FontCacheEntry::FontCacheEntry() 131 : 132 MultiLocker("FontCacheEntry lock"), 133 fGlyphCache(new(std::nothrow) GlyphCachePool()), 134 fEngine(), 135 fLastUsedTime(LONGLONG_MIN), 136 fUseCounter(0) 137 { 138 } 139 140 141 FontCacheEntry::~FontCacheEntry() 142 { 143 //printf("~FontCacheEntry()\n"); 144 delete fGlyphCache; 145 } 146 147 148 bool 149 FontCacheEntry::Init(const ServerFont& font) 150 { 151 if (fGlyphCache == NULL) 152 return false; 153 154 glyph_rendering renderingType = _RenderTypeFor(font); 155 156 // TODO: encoding from font 157 FT_Encoding charMap = FT_ENCODING_NONE; 158 bool hinting = font.Hinting(); 159 160 if (!fEngine.Init(font.Path(), 0, font.Size(), charMap, 161 renderingType, hinting)) { 162 fprintf(stderr, "FontCacheEntry::Init() - some error loading font " 163 "file %s\n", font.Path()); 164 return false; 165 } 166 if (fGlyphCache->Init() != B_OK) { 167 fprintf(stderr, "FontCacheEntry::Init() - failed to allocate " 168 "GlyphCache table for font file %s\n", font.Path()); 169 return false; 170 } 171 172 return true; 173 } 174 175 176 bool 177 FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const 178 { 179 uint32 glyphCode; 180 const char* start = utf8String; 181 while ((glyphCode = UTF8ToCharCode(&utf8String))) { 182 if (fGlyphCache->FindGlyph(glyphCode) == NULL) 183 return false; 184 if (utf8String - start + 1 > length) 185 break; 186 } 187 return true; 188 } 189 190 191 inline bool 192 render_as_space(uint32 glyphCode) 193 { 194 // whitespace: render as space 195 // as per Unicode PropList.txt: White_Space 196 return (glyphCode >= 0x0009 && glyphCode <= 0x000d) 197 // control characters 198 || (glyphCode == 0x0085) 199 // another control 200 || (glyphCode == 0x00a0) 201 // no-break space 202 || (glyphCode == 0x1680) 203 // ogham space mark 204 || (glyphCode == 0x180e) 205 // mongolian vowel separator 206 || (glyphCode >= 0x2000 && glyphCode <= 0x200a) 207 // en quand, hair space 208 || (glyphCode >= 0x2028 && glyphCode <= 0x2029) 209 // line and paragraph separators 210 || (glyphCode == 0x202f) 211 // narrow no-break space 212 || (glyphCode == 0x205f) 213 // medium math space 214 || (glyphCode == 0x3000) 215 // ideographic space 216 ; 217 } 218 219 220 inline bool 221 render_as_zero_width(uint32 glyphCode) 222 { 223 // ignorable chars: render as invisible 224 // as per Unicode DerivedCoreProperties.txt: Default_Ignorable_Code_Point 225 return (glyphCode == 0x00ad) 226 // soft hyphen 227 || (glyphCode == 0x034f) 228 // combining grapheme joiner 229 || (glyphCode >= 0x115f && glyphCode <= 0x1160) 230 // hangul fillers 231 || (glyphCode >= 0x17b4 && glyphCode <= 0x17b5) 232 // ignorable khmer vowels 233 || (glyphCode >= 0x180b && glyphCode <= 0x180d) 234 // variation selectors 235 || (glyphCode >= 0x200b && glyphCode <= 0x200f) 236 // zero width space, cursive joiners, ltr marks 237 || (glyphCode >= 0x202a && glyphCode <= 0x202e) 238 // left to right embed, override 239 || (glyphCode >= 0x2060 && glyphCode <= 0x206f) 240 // word joiner, invisible math operators, reserved 241 || (glyphCode == 0x3164) 242 // hangul filler 243 || (glyphCode >= 0xfe00 && glyphCode <= 0xfe0f) 244 // variation selectors 245 || (glyphCode == 0xfeff) 246 // zero width no-break space 247 || (glyphCode == 0xffa0) 248 // halfwidth hangul filler 249 || (glyphCode >= 0xfff0 && glyphCode <= 0xfff8) 250 // reserved 251 || (glyphCode >= 0x1d173 && glyphCode <= 0x1d17a) 252 // musical symbols 253 || (glyphCode >= 0xe0000 && glyphCode <= 0xe01ef) 254 // variation selectors, tag space, reserved 255 ; 256 } 257 258 259 const GlyphCache* 260 FontCacheEntry::CachedGlyph(uint32 glyphCode) 261 { 262 // Only requires a read lock. 263 return fGlyphCache->FindGlyph(glyphCode); 264 } 265 266 267 const GlyphCache* 268 FontCacheEntry::CreateGlyph(uint32 glyphCode, FontCacheEntry* fallbackEntry) 269 { 270 // We cache the glyph by the requested glyphCode. The FontEngine of this 271 // FontCacheEntry may not contain a glyph for the given code, in which case 272 // we ask the fallbackEntry for the code to index translation and let it 273 // generate the glyph data. We will still use our own cache for storing the 274 // glyph. The next time it will be found (by glyphCode). 275 276 // NOTE: Both this and the fallback FontCacheEntry are expected to be 277 // write-locked! 278 279 const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode); 280 if (glyph != NULL) 281 return glyph; 282 283 FontEngine* engine = &fEngine; 284 uint32 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode); 285 if (glyphIndex == 0 && fallbackEntry != NULL) { 286 // Our FontEngine does not contain this glyph, but we can retry with 287 // the fallbackEntry. 288 engine = &fallbackEntry->fEngine; 289 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode); 290 } 291 292 if (glyphIndex == 0) { 293 if (render_as_zero_width(glyphCode)) { 294 // cache and return a zero width glyph 295 return fGlyphCache->CacheGlyph(glyphCode, 0, glyph_data_invalid, 296 agg::rect_i(0, 0, -1, -1), 0, 0, 0, 0); 297 } 298 299 // reset to our engine 300 engine = &fEngine; 301 if (render_as_space(glyphCode)) { 302 // get the normal space glyph 303 glyphIndex = engine->GlyphIndexForGlyphCode(0x20 /* space */); 304 } else { 305 // render the "missing glyph box" (by simply keeping glyphIndex 0) 306 } 307 } 308 309 if (engine->PrepareGlyph(glyphIndex)) { 310 glyph = fGlyphCache->CacheGlyph(glyphCode, 311 engine->DataSize(), engine->DataType(), engine->Bounds(), 312 engine->AdvanceX(), engine->AdvanceY(), 313 engine->InsetLeft(), engine->InsetRight()); 314 315 if (glyph != NULL) 316 engine->WriteGlyphTo(glyph->data); 317 } 318 319 return glyph; 320 } 321 322 323 void 324 FontCacheEntry::InitAdaptors(const GlyphCache* glyph, 325 double x, double y, GlyphMonoAdapter& monoAdapter, 326 GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter, 327 double scale) 328 { 329 if (!glyph) 330 return; 331 332 switch(glyph->data_type) { 333 case glyph_data_mono: 334 monoAdapter.init(glyph->data, glyph->data_size, x, y); 335 break; 336 337 case glyph_data_gray8: 338 gray8Adapter.init(glyph->data, glyph->data_size, x, y); 339 break; 340 341 case glyph_data_subpix: 342 gray8Adapter.init(glyph->data, glyph->data_size, x, y); 343 break; 344 345 case glyph_data_outline: 346 pathAdapter.init(glyph->data, glyph->data_size, x, y, scale); 347 break; 348 349 default: 350 break; 351 } 352 } 353 354 355 bool 356 FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2, 357 double* x, double* y) 358 { 359 return fEngine.GetKerning(glyphCode1, glyphCode2, x, y); 360 } 361 362 363 /*static*/ void 364 FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize, 365 const ServerFont& font) 366 { 367 glyph_rendering renderingType = _RenderTypeFor(font); 368 369 // TODO: read more of these from the font 370 FT_Encoding charMap = FT_ENCODING_NONE; 371 bool hinting = font.Hinting(); 372 uint8 averageWeight = gSubpixelAverageWeight; 373 374 snprintf(signature, signatureSize, "%" B_PRId32 ",%u,%d,%d,%.1f,%d,%d", 375 font.GetFamilyAndStyle(), charMap, 376 font.Face(), int(renderingType), font.Size(), hinting, averageWeight); 377 } 378 379 380 void 381 FontCacheEntry::UpdateUsage() 382 { 383 // this is a static lock to prevent usage of too many semaphores, 384 // but on the other hand, it is not so nice to be using a lock 385 // here at all 386 // the hope is that the time is so short to hold this lock, that 387 // there is not much contention 388 BAutolock _(sUsageUpdateLock); 389 390 fLastUsedTime = system_time(); 391 fUseCounter++; 392 } 393 394 395 /*static*/ glyph_rendering 396 FontCacheEntry::_RenderTypeFor(const ServerFont& font) 397 { 398 glyph_rendering renderingType = gSubpixelAntialiasing ? 399 glyph_ren_subpix : glyph_ren_native_gray8; 400 401 if (font.Rotation() != 0.0 || font.Shear() != 90.0 402 || font.FalseBoldWidth() != 0.0 403 || (font.Flags() & B_DISABLE_ANTIALIASING) != 0 404 || font.Size() > 30 405 || !font.Hinting()) { 406 renderingType = glyph_ren_outline; 407 } 408 409 return renderingType; 410 } 411