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 42BLocker FontCacheEntry::sUsageUpdateLock("FontCacheEntry usage lock"); 43 44 45class 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 }; 72public: 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 preciseAdvanceX, 100 float preciseAdvanceY, float insetLeft, float insetRight) 101 { 102 GlyphCache* glyph = fGlyphTable.Lookup(glyphIndex); 103 if (glyph != NULL) 104 return NULL; 105 106 glyph = new(std::nothrow) GlyphCache(glyphIndex, dataSize, dataType, 107 bounds, advanceX, advanceY, preciseAdvanceX, preciseAdvanceY, 108 insetLeft, insetRight); 109 if (glyph == NULL || glyph->data == NULL) { 110 delete glyph; 111 return NULL; 112 } 113 114 // TODO: The HashTable grows without bounds. We should cleanup 115 // older entries from time to time. 116 117 fGlyphTable.Insert(glyph); 118 119 return glyph; 120 } 121 122private: 123 typedef BOpenHashTable<GlyphHashTableDefinition> GlyphTable; 124 125 GlyphTable fGlyphTable; 126}; 127 128 129// #pragma mark - 130 131 132FontCacheEntry::FontCacheEntry() 133 : 134 MultiLocker("FontCacheEntry lock"), 135 fGlyphCache(new(std::nothrow) GlyphCachePool()), 136 fEngine(), 137 fLastUsedTime(LONGLONG_MIN), 138 fUseCounter(0) 139{ 140} 141 142 143FontCacheEntry::~FontCacheEntry() 144{ 145//printf("~FontCacheEntry()\n"); 146} 147 148 149bool 150FontCacheEntry::Init(const ServerFont& font, bool forceVector) 151{ 152 if (!fGlyphCache.IsSet()) 153 return false; 154 155 glyph_rendering renderingType = _RenderTypeFor(font, forceVector); 156 157 // TODO: encoding from font 158 FT_Encoding charMap = FT_ENCODING_NONE; 159 bool hinting = font.Hinting(); 160 161 bool success; 162 if (font.FontData() != NULL) 163 success = fEngine.Init(NULL, font.FaceIndex(), font.Size(), charMap, 164 renderingType, hinting, (const void*)font.FontData(), font.FontDataSize()); 165 else 166 success = fEngine.Init(font.Path(), font.FaceIndex(), font.Size(), charMap, 167 renderingType, hinting); 168 169 if (!success) { 170 fprintf(stderr, "FontCacheEntry::Init() - some error loading font " 171 "file %s\n", font.Path()); 172 return false; 173 } 174 175 if (fGlyphCache->Init() != B_OK) { 176 fprintf(stderr, "FontCacheEntry::Init() - failed to allocate " 177 "GlyphCache table for font file %s\n", font.Path()); 178 return false; 179 } 180 181 return true; 182} 183 184 185bool 186FontCacheEntry::HasGlyphs(const char* utf8String, ssize_t length) const 187{ 188 uint32 glyphCode; 189 const char* start = utf8String; 190 while ((glyphCode = UTF8ToCharCode(&utf8String))) { 191 if (fGlyphCache->FindGlyph(glyphCode) == NULL) 192 return false; 193 if (utf8String - start + 1 > length) 194 break; 195 } 196 return true; 197} 198 199 200inline bool 201render_as_space(uint32 glyphCode) 202{ 203 // whitespace: render as space 204 // as per Unicode PropList.txt: White_Space 205 return (glyphCode >= 0x0009 && glyphCode <= 0x000d) 206 // control characters 207 || (glyphCode == 0x0085) 208 // another control 209 || (glyphCode == 0x00a0) 210 // no-break space 211 || (glyphCode == 0x1680) 212 // ogham space mark 213 || (glyphCode >= 0x2000 && glyphCode <= 0x200a) 214 // en quand, hair space 215 || (glyphCode >= 0x2028 && glyphCode <= 0x2029) 216 // line and paragraph separators 217 || (glyphCode == 0x202f) 218 // narrow no-break space 219 || (glyphCode == 0x205f) 220 // medium math space 221 || (glyphCode == 0x3000) 222 // ideographic space 223 ; 224} 225 226 227inline bool 228render_as_zero_width(uint32 glyphCode) 229{ 230 // ignorable chars: render as invisible 231 // as per Unicode DerivedCoreProperties.txt: Default_Ignorable_Code_Point. 232 // We also don't want tofu for noncharacters if we ever get one. 233 return (glyphCode == 0x00ad) 234 // soft hyphen 235 || (glyphCode == 0x034f) 236 // combining grapheme joiner 237 || (glyphCode == 0x061c) 238 // arabic letter mark 239 || (glyphCode >= 0x115f && glyphCode <= 0x1160) 240 // hangul fillers 241 || (glyphCode >= 0x17b4 && glyphCode <= 0x17b5) 242 // ignorable khmer vowels 243 || (glyphCode >= 0x180b && glyphCode <= 0x180f) 244 // mongolian variation selectors and vowel separator 245 || (glyphCode >= 0x200b && glyphCode <= 0x200f) 246 // zero width space, cursive joiners, ltr marks 247 || (glyphCode >= 0x202a && glyphCode <= 0x202e) 248 // left to right embed, override 249 || (glyphCode >= 0x2060 && glyphCode <= 0x206f) 250 // word joiner, invisible math operators, reserved 251 || (glyphCode == 0x3164) 252 // hangul filler 253 || (glyphCode >= 0xfe00 && glyphCode <= 0xfe0f) 254 // variation selectors 255 || (glyphCode == 0xfeff) 256 // zero width no-break space 257 || (glyphCode == 0xffa0) 258 // halfwidth hangul filler 259 || (glyphCode >= 0xfff0 && glyphCode <= 0xfff8) 260 // reserved 261 || (glyphCode >= 0x1bca0 && glyphCode <= 0x1bca3) 262 // shorthand format controls 263 || (glyphCode >= 0x1d173 && glyphCode <= 0x1d17a) 264 // musical symbols 265 || (glyphCode >= 0xe0000 && glyphCode <= 0xe01ef) 266 // variation selectors, tag space, reserved 267 || (glyphCode >= 0xe01f0 && glyphCode <= 0xe0fff) 268 // reserved 269 || ((glyphCode & 0xffff) >= 0xfffe) 270 // noncharacters 271 || ((glyphCode >= 0xfdd0 && glyphCode <= 0xfdef) 272 && glyphCode != 0xfdd1) 273 // noncharacters; 0xfdd1 is used internally to force .notdef glyph 274 ; 275} 276 277 278const GlyphCache* 279FontCacheEntry::CachedGlyph(uint32 glyphCode) 280{ 281 // Only requires a read lock. 282 return fGlyphCache->FindGlyph(glyphCode); 283} 284 285 286bool 287FontCacheEntry::CanCreateGlyph(uint32 glyphCode) 288{ 289 // Note that this bypass any fallback or caching because it is used in 290 // the fallback code itself. 291 uint32 glyphIndex = fEngine.GlyphIndexForGlyphCode(glyphCode); 292 return glyphIndex != 0; 293} 294 295 296const GlyphCache* 297FontCacheEntry::CreateGlyph(uint32 glyphCode, FontCacheEntry* fallbackEntry) 298{ 299 // We cache the glyph by the requested glyphCode. The FontEngine of this 300 // FontCacheEntry may not contain a glyph for the given code, in which case 301 // we ask the fallbackEntry for the code to index translation and let it 302 // generate the glyph data. We will still use our own cache for storing the 303 // glyph. The next time it will be found (by glyphCode). 304 305 // NOTE: Both this and the fallback FontCacheEntry are expected to be 306 // write-locked! 307 308 const GlyphCache* glyph = fGlyphCache->FindGlyph(glyphCode); 309 if (glyph != NULL) 310 return glyph; 311 312 FontEngine* engine = &fEngine; 313 uint32 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode); 314 if (glyphIndex == 0 && fallbackEntry != NULL) { 315 // Our FontEngine does not contain this glyph, but we can retry with 316 // the fallbackEntry. 317 engine = &fallbackEntry->fEngine; 318 glyphIndex = engine->GlyphIndexForGlyphCode(glyphCode); 319 } 320 321 if (glyphIndex == 0) { 322 if (render_as_zero_width(glyphCode)) { 323 // cache and return a zero width glyph 324 return fGlyphCache->CacheGlyph(glyphCode, 0, glyph_data_invalid, 325 agg::rect_i(0, 0, -1, -1), 0, 0, 0, 0, 0, 0); 326 } 327 328 // reset to our engine 329 engine = &fEngine; 330 if (render_as_space(glyphCode)) { 331 // get the normal space glyph 332 glyphIndex = engine->GlyphIndexForGlyphCode(0x20 /* space */); 333 } 334 } 335 336 if (engine->PrepareGlyph(glyphIndex)) { 337 glyph = fGlyphCache->CacheGlyph(glyphCode, 338 engine->DataSize(), engine->DataType(), engine->Bounds(), 339 engine->AdvanceX(), engine->AdvanceY(), 340 engine->PreciseAdvanceX(), engine->PreciseAdvanceY(), 341 engine->InsetLeft(), engine->InsetRight()); 342 343 if (glyph != NULL) 344 engine->WriteGlyphTo(glyph->data); 345 } 346 347 return glyph; 348} 349 350 351void 352FontCacheEntry::InitAdaptors(const GlyphCache* glyph, 353 double x, double y, GlyphMonoAdapter& monoAdapter, 354 GlyphGray8Adapter& gray8Adapter, GlyphPathAdapter& pathAdapter, 355 double scale) 356{ 357 if (glyph == NULL) 358 return; 359 360 switch(glyph->data_type) { 361 case glyph_data_mono: 362 monoAdapter.init(glyph->data, glyph->data_size, x, y); 363 break; 364 365 case glyph_data_gray8: 366 gray8Adapter.init(glyph->data, glyph->data_size, x, y); 367 break; 368 369 case glyph_data_subpix: 370 gray8Adapter.init(glyph->data, glyph->data_size, x, y); 371 break; 372 373 case glyph_data_outline: 374 pathAdapter.init(glyph->data, glyph->data_size, x, y, scale); 375 break; 376 377 default: 378 break; 379 } 380} 381 382 383bool 384FontCacheEntry::GetKerning(uint32 glyphCode1, uint32 glyphCode2, 385 double* x, double* y) 386{ 387 return fEngine.GetKerning(glyphCode1, glyphCode2, x, y); 388} 389 390 391/*static*/ void 392FontCacheEntry::GenerateSignature(char* signature, size_t signatureSize, 393 const ServerFont& font, bool forceVector) 394{ 395 glyph_rendering renderingType = _RenderTypeFor(font, forceVector); 396 397 // TODO: read more of these from the font 398 FT_Encoding charMap = FT_ENCODING_NONE; 399 bool hinting = font.Hinting(); 400 uint8 averageWeight = gSubpixelAverageWeight; 401 402 snprintf(signature, signatureSize, "%" B_PRId32 ",%p,%u,%d,%d,%.1f,%d,%d", 403 font.GetFamilyAndStyle(), font.Manager(), charMap, 404 font.Face(), int(renderingType), font.Size(), hinting, averageWeight); 405} 406 407 408void 409FontCacheEntry::UpdateUsage() 410{ 411 // this is a static lock to prevent usage of too many semaphores, 412 // but on the other hand, it is not so nice to be using a lock 413 // here at all 414 // the hope is that the time is so short to hold this lock, that 415 // there is not much contention 416 BAutolock _(sUsageUpdateLock); 417 418 fLastUsedTime = system_time(); 419 fUseCounter++; 420} 421 422 423/*static*/ glyph_rendering 424FontCacheEntry::_RenderTypeFor(const ServerFont& font, bool forceVector) 425{ 426 glyph_rendering renderingType = gSubpixelAntialiasing ? 427 glyph_ren_subpix : glyph_ren_native_gray8; 428 429 if (forceVector || font.Rotation() != 0.0 || font.Shear() != 90.0 430 || font.FalseBoldWidth() != 0.0 431 || (font.Flags() & B_DISABLE_ANTIALIASING) != 0 432 || font.Size() > 30 433 || !font.Hinting()) { 434 renderingType = glyph_ren_outline; 435 } 436 437 return renderingType; 438} 439