1 // Written in the D programming language. 2 3 /** 4 DLANGUI library. 5 6 This module contains base fonts access implementation. 7 8 To enable OpenGL support, build with version(USE_OPENGL); 9 10 Synopsis: 11 12 ---- 13 import dlangui.graphics.glsupport; 14 15 ---- 16 17 Copyright: Vadim Lopatin, 2014 18 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 19 Authors: $(WEB coolreader.org, Vadim Lopatin) 20 */ 21 module dlangui.graphics.fonts; 22 public import dlangui.graphics.drawbuf; 23 public import dlangui.core.types; 24 public import dlangui.core.logger; 25 import std.algorithm; 26 27 /// font family 28 enum FontFamily : ubyte { 29 Unspecified, 30 SansSerif, 31 Serif, 32 Fantasy, 33 Cursive, 34 MonoSpace 35 } 36 37 /// useful font weight constants 38 enum FontWeight : int { 39 Normal = 400, 40 Bold = 800 41 } 42 43 const dchar UNICODE_SOFT_HYPHEN_CODE = 0x00ad; 44 const dchar UNICODE_ZERO_WIDTH_SPACE = 0x200b; 45 const dchar UNICODE_NO_BREAK_SPACE = 0x00a0; 46 const dchar UNICODE_HYPHEN = 0x2010; 47 const dchar UNICODE_NB_HYPHEN = 0x2011; 48 49 version (USE_OPENGL) { 50 private __gshared void function(uint id) _glyphDestroyCallback; 51 /// get glyph destroy callback (to cleanup OpenGL caches) 52 @property void function(uint id) glyphDestroyCallback() { return _glyphDestroyCallback; } 53 /// set glyph destroy callback (to cleanup OpenGL caches) 54 @property void glyphDestroyCallback(void function(uint id) callback) { _glyphDestroyCallback = callback; } 55 56 private __gshared uint _nextGlyphId; 57 uint nextGlyphId() { return _nextGlyphId++; } 58 } 59 60 /// font glyph cache 61 struct GlyphCache 62 { 63 //Glyph[ushort] _map; 64 Glyph[][256] _glyphs; 65 66 //Glyph[] _array; 67 68 // find glyph in cache 69 Glyph * find(ushort glyphIndex) { 70 //if (_array is null) 71 // _array = new Glyph[0x10000]; 72 ushort p = glyphIndex >> 8; 73 if (_glyphs[p] is null) 74 return null; 75 ushort i = glyphIndex & 0xFF; 76 if (_glyphs[p][i].glyphIndex == 0) 77 return null; 78 return &_glyphs[p][i]; 79 //if (_array[glyphIndex].glyphIndex) 80 // return &_array[glyphIndex]; 81 //return null; 82 83 //Glyph * res = (glyphIndex in _map); 84 //if (res !is null) 85 // res.lastUsage = 1; 86 //return res; 87 } 88 89 /// put glyph to cache 90 Glyph * put(ushort glyphIndex, Glyph * glyph) { 91 ushort p = glyphIndex >> 8; 92 ushort i = glyphIndex & 0xFF; 93 if (_glyphs[p] is null) 94 _glyphs[p] = new Glyph[256]; 95 _glyphs[p][i] = *glyph; 96 return &_glyphs[p][i]; // = *glyph; 97 98 //_array[glyphIndex] = *glyph; 99 //return &_array[glyphIndex]; 100 101 //_map[glyphIndex] = *glyph; 102 //Glyph * res = glyphIndex in _map; 103 //res.lastUsage = 1; 104 //return res; 105 } 106 107 // clear usage flags for all entries 108 void checkpoint() { 109 //foreach(ref Glyph item; _map) { 110 // item.lastUsage = 0; 111 //} 112 foreach(ref Glyph[] part; _glyphs) { 113 if (part !is null) 114 foreach(ref Glyph item; part) { 115 item.lastUsage = 0; 116 } 117 } 118 //foreach(ref Glyph item; _array) { 119 // item.lastUsage = 0; 120 //} 121 } 122 123 /// removes entries not used after last call of checkpoint() or cleanup() 124 void cleanup() { 125 //uint dst = 0; 126 // notify about destroyed glyphs 127 version (USE_OPENGL) { 128 if (_glyphDestroyCallback !is null) { 129 foreach(ref Glyph[] part; _glyphs) { 130 if (part !is null) 131 foreach(ref Glyph item; part) { 132 if (item.lastUsage == 0 && item.glyphIndex) 133 _glyphDestroyCallback(item.id); 134 } 135 } 136 //foreach(ref Glyph item; _map) { 137 // if (item.lastUsage == 0) 138 // _glyphDestroyCallback(item.id); 139 //} 140 } 141 } 142 //ushort[] forDelete; 143 //foreach(ref Glyph item; _map) 144 // if (item.lastUsage == 0) 145 // forDelete ~= item.glyphIndex; 146 //foreach(ushort index; forDelete) 147 // _map.remove(index); 148 foreach(ref Glyph[] part; _glyphs) { 149 if (part !is null) 150 foreach(ref Glyph item; part) { 151 if (item.lastUsage == 0 && item.glyphIndex) { 152 item.glyphIndex = 0; 153 item.glyph = null; 154 version (USE_OPENGL) { 155 item.id = 0; 156 } 157 } 158 } 159 } 160 //foreach(ref Glyph item; _array) { 161 // if (item.lastUsage == 0 && item.glyphIndex) { 162 // item.glyphIndex = 0; 163 // item.glyph = null; 164 // item.id = 0; 165 // } 166 //} 167 } 168 169 /// removes all entries 170 void clear() { 171 // notify about destroyed glyphs 172 version (USE_OPENGL) { 173 if (_glyphDestroyCallback !is null) { 174 foreach(ref Glyph[] part; _glyphs) { 175 if (part !is null) 176 foreach(ref Glyph item; part) { 177 if (item.glyphIndex) 178 _glyphDestroyCallback(item.id); 179 } 180 } 181 } 182 } 183 foreach(ref Glyph[] part; _glyphs) { 184 if (part !is null) 185 foreach(ref Glyph item; part) { 186 if (item.glyphIndex) { 187 item.glyphIndex = 0; 188 item.glyph = null; 189 version (USE_OPENGL) { 190 item.id = 0; 191 } 192 } 193 } 194 } 195 196 //version (USE_OPENGL) { 197 // if (_glyphDestroyCallback !is null) { 198 // foreach(ref Glyph item; _array) { 199 // if (item.glyphIndex) 200 // _glyphDestroyCallback(item.id); 201 // } 202 // //foreach(ref Glyph item; _map) { 203 // // if (item.lastUsage == 0) 204 // // _glyphDestroyCallback(item.id); 205 // //} 206 // } 207 //} 208 ////_map.clear(); 209 //foreach(ref Glyph item; _array) { 210 // if (item.glyphIndex) { 211 // item.glyphIndex = 0; 212 // item.glyph = null; 213 // item.id = 0; 214 // } 215 //} 216 } 217 ~this() { 218 clear(); 219 } 220 } 221 222 /// Font object 223 class Font : RefCountedObject { 224 abstract @property int size(); 225 abstract @property int height(); 226 abstract @property int weight(); 227 abstract @property int baseline(); 228 abstract @property bool italic(); 229 abstract @property string face(); 230 abstract @property FontFamily family(); 231 abstract @property bool isNull(); 232 // measure text string, return accumulated widths[] (distance to end of n-th character), returns number of measured chars. 233 abstract int measureText(const dchar[] text, ref int[] widths, int maxWidth); 234 private int[] _textSizeBuffer; // to avoid GC 235 // measure text string as single line, returns width and height 236 Point textSize(const dchar[] text, int maxWidth = 3000) { 237 if (_textSizeBuffer.length < text.length + 1) 238 _textSizeBuffer.length = text.length + 1; 239 //int[] widths = new int[text.length + 1]; 240 int charsMeasured = measureText(text, _textSizeBuffer, maxWidth); 241 if (charsMeasured < 1) 242 return Point(0,0); 243 return Point(_textSizeBuffer[charsMeasured - 1], height); 244 } 245 /// draw text string to buffer 246 abstract void drawText(DrawBuf buf, int x, int y, const dchar[] text, uint color); 247 /// get character glyph information 248 abstract Glyph * getCharGlyph(dchar ch, bool withImage = true); 249 250 /// clear usage flags for all entries 251 abstract void checkpoint(); 252 /// removes entries not used after last call of checkpoint() or cleanup() 253 abstract void cleanup(); 254 255 void clear() {} 256 257 ~this() { clear(); } 258 } 259 alias FontRef = Ref!Font; 260 261 /// font instance collection - utility class, for font manager implementations 262 struct FontList { 263 FontRef[] _list; 264 uint _len; 265 ~this() { 266 clear(); 267 } 268 269 @property uint length() { 270 return _len; 271 } 272 273 void clear() { 274 for (uint i = 0; i < _len; i++) { 275 _list[i].clear(); 276 _list[i] = null; 277 } 278 _len = 0; 279 } 280 // returns item by index 281 ref FontRef get(int index) { 282 return _list[index]; 283 } 284 // find by a set of parameters - returns index of found item, -1 if not found 285 int find(int size, int weight, bool italic, FontFamily family, string face) { 286 for (int i = 0; i < _len; i++) { 287 Font item = _list[i].get; 288 if (item.family != family) 289 continue; 290 if (item.size != size) 291 continue; 292 if (item.italic != italic || item.weight != weight) 293 continue; 294 if (!equal(item.face, face)) 295 continue; 296 return i; 297 } 298 return -1; 299 } 300 // find by size only - returns index of found item, -1 if not found 301 int find(int size) { 302 for (int i = 0; i < _len; i++) { 303 Font item = _list[i].get; 304 if (item.size != size) 305 continue; 306 return i; 307 } 308 return -1; 309 } 310 ref FontRef add(Font item) { 311 Log.d("FontList.add() enter"); 312 if (_len >= _list.length) { 313 _list.length = _len < 16 ? 16 : _list.length * 2; 314 } 315 _list[_len++] = item; 316 Log.d("FontList.add() exit"); 317 return _list[_len - 1]; 318 } 319 // remove unused items - with reference == 1 320 void cleanup() { 321 for (int i = 0; i < _len; i++) 322 if (_list[i].refCount <= 1) 323 _list[i].clear(); 324 int dst = 0; 325 for (int i = 0; i < _len; i++) { 326 if (!_list[i].isNull) 327 if (i != dst) 328 _list[dst++] = _list[i]; 329 } 330 _len = dst; 331 for (int i = 0; i < _len; i++) 332 _list[i].cleanup(); 333 } 334 void checkpoint() { 335 for (int i = 0; i < _len; i++) 336 _list[i].checkpoint(); 337 } 338 } 339 340 341 /// Access points to fonts. 342 class FontManager { 343 protected static __gshared FontManager _instance; 344 345 /// sets new font manager singleton instance 346 static @property void instance(FontManager manager) { 347 if (_instance !is null) { 348 destroy(_instance); 349 _instance = null; 350 } 351 _instance = manager; 352 } 353 354 /// returns font manager singleton instance 355 static @property FontManager instance() { 356 return _instance; 357 } 358 359 /// get font instance best matched specified parameters 360 abstract ref FontRef getFont(int size, int weight, bool italic, FontFamily family, string face); 361 362 /// clear usage flags for all entries -- for cleanup of unused fonts 363 abstract void checkpoint(); 364 365 /// removes entries not used after last call of checkpoint() or cleanup() 366 abstract void cleanup(); 367 368 ~this() { 369 Log.d("Destroying font manager"); 370 } 371 }