1 #ifdef USEFREETYPE
2
3 #include <unordered_map>
4
5 namespace FTU
6 {
7
8 Mutex mutex;
9 static int libState = -1;
10 static FT_Library library = 0;
11
CheckLib()12 inline bool CheckLib()
13 {
14 if ( libState < 0 )
15 {
16 libState = FT_Init_FreeType( &library ) ? 1 : 0;
17 }
18
19 return libState == 0;
20 }
21
22 struct CharInfo
23 {
24 bool isSpace;
25 int pxWidth;
26
CharInfoCharInfo27 CharInfo( FT_GlyphSlotRec* p )
28 {
29 pxWidth = ( ( p->metrics.horiAdvance + 0x3F ) >> 6 );
30 isSpace = !p->bitmap.buffer || p->bitmap.width <= 0 || p->bitmap.rows <= 0;
31 };
CharInfoCharInfo32 CharInfo() {}
33 };
34
35
36 //кэш изображений (битмапов)
37 class ImCache
38 {
39 enum CONST { MAX_IMAGE_WIDTH = 640, MAX_IMAGE_HEIGHT = 480 };
40
41 struct NK
42 {
43 unsigned bg; //цвет фона
44 unsigned fg; //цвет символа
45 unicode_t ch; //символ
46
operatorNK47 std::size_t operator()( const NK& k ) const { return k.bg + k.fg + k.ch; }
48
49 bool operator == ( const NK& a ) const { return ch == a.ch && bg == a.bg && fg == a.fg; }
50 };
51
52 struct XYWH
53 {
54 int x, y, w, h;
XYWHXYWH55 XYWH() {}
XYWHXYWH56 XYWH( int _x, int _y, int _w, int _h ): x( _x ), y( _y ), w( _w ), h( _h ) {}
SetXYWH57 void Set( int _x, int _y, int _w, int _h ) { x = _x; y = _y; w = _w; h = _h; }
58 };
59
60 struct Node
61 {
62 NK k;
63 XYWH place;
64 };
65
66 std::unordered_map<NK, Node, NK> nodesHash;
67
68 SCImage image;
69 int xPos;
70 int yPos;
71 int cHeight;
72
73 Node* Alloc( unicode_t ch, unsigned bg, unsigned fg, int charW, int charH );
74 void CopyArea( wal::GC& gc, Node* node, int x, int y );
75
76 void DrawImage( wal::GC& gc, Image32& im, int x, int y );
77
78 public:
79 ImCache();
80 void Clear();
81
82 //нарисовать если есть в кэше
83 bool DrawIfExist( wal::GC& gc, int x, int y, unicode_t ch, unsigned bg, unsigned fg );
84
85 //нарисовать и поместить в кэш если получится
86 void Draw( wal::GC& gc, int x, int y, unicode_t ch, unsigned bg, unsigned fg, Image32& im );
87
88 ~ImCache();
89 };
90
91 struct ColorData //чтоб не считать постоянно наложения цветов фона и текста
92 {
93 bool changed;
94 unsigned bg;
95 unsigned fg;
96 unsigned colors[0x100];
97 char bools[0x100];
98
ColorDataColorData99 ColorData(): changed( true ), bg( 0 ), fg( 0xFFFFFF ) {}
SetBgColorData100 void SetBg( unsigned c ) { if ( c != bg ) { bg = c; changed = true; } }
SetFgColorData101 void SetFg( unsigned c ) { if ( c != fg ) { fg = c; changed = true; } }
PrepareColorData102 void Prepare() { if ( changed ) { memset( bools, 0, 0x100 ); changed = false; } }
IsSetColorData103 bool IsSet( int n ) { return bools[n]; }
GetColorData104 unsigned Get( int n ) { return colors[n]; }
SetColorData105 void Set( int n, unsigned c ) { bools[n] = 1; colors[n] = c; }
106 };
107
108
109 class FFace
110 {
111 FT_Face face;
112 ColorData cData;
113 ImCache imCache;
114
115 int pxAscender;
116 int pxHeight;
117 int _size;
118
119 std::unordered_map<unicode_t, CharInfo> ciHash;
120
Clear()121 void Clear() { if ( face ) { FT_Done_Face( face ); face = 0; imCache.Clear(); ciHash.clear(); } }
122
123 int SetSize( int size, int xRes, int yRes );
124 bool MkImage( Image32& im, FT_GlyphSlot slot );
125 void OutChar( wal::GC& gc, int* px, int* py, unicode_t c );
126 void OutCharF( wal::GC& gc, int* px, int* py, unicode_t c );
127
128 public:
129 FFace();
130 int Load( const char* fileName, int index, int size, int xRes = 100, int yRes = 100 );
131 void OutText( wal::GC& gc, int x, int y, const unicode_t* text, int count );
132 void OutTextF( wal::GC& gc, int x, int y, const unicode_t* text, int count );
133 cpoint GetTextExtents( const unicode_t* text, int count );
134
Name()135 const char* Name() { return face ? face->family_name : nullptr; }
StyleName()136 const char* StyleName() { return face ? face->style_name : nullptr; }
FaceFlags()137 unsigned FaceFlags() { return face ? face->face_flags : 0; }
StyleFlags()138 unsigned StyleFlags() { return face ? face->style_flags : 0; }
Size()139 int Size() { return _size; }
140
PxHeight()141 int PxHeight() { return pxHeight; }
142 ~FFace();
143 };
144
145
146
147 ////////////////////// ImCache //////////////////////////////////////////////////////////////////
148
ImCache()149 ImCache::ImCache(): xPos( 0 ), yPos( 0 ), cHeight( 0 ) {}
Clear()150 void ImCache::Clear() { nodesHash.clear(); xPos = 0; yPos = 0; cHeight = 0; }
151
CopyArea(wal::GC & gc,Node * p,int x,int y)152 inline void ImCache::CopyArea( wal::GC& gc, Node* p, int x, int y )
153 {
154 XCopyArea( display, image.GetXDrawable(), gc.GetXDrawable(), gc.XHandle(), p->place.x, p->place.y, p->place.w, p->place.h, x, y );
155 }
156
DrawIfExist(wal::GC & gc,int x,int y,unicode_t ch,unsigned bg,unsigned fg)157 bool ImCache::DrawIfExist( wal::GC& gc, int x, int y, unicode_t ch, unsigned bg, unsigned fg )
158 {
159 NK k;
160 k.bg = bg;
161 k.fg = fg;
162 k.ch = ch;
163
164 const auto& i = nodesHash.find( k );
165
166 Node* p = ( i == nodesHash.end() ) ? nullptr : &(i->second);
167
168 if ( !p ) { return false; }
169
170 CopyArea( gc, p, x, y );
171
172 return true;
173 }
174
Alloc(unicode_t ch,unsigned bg,unsigned fg,int charW,int charH)175 ImCache::Node* ImCache::Alloc( unicode_t ch, unsigned bg, unsigned fg, int charW, int charH )
176 {
177 //return 0;
178
179 if ( charW > MAX_IMAGE_WIDTH || charH > MAX_IMAGE_HEIGHT ) { return 0; }
180
181 if ( image.Width() - xPos < charW )
182 {
183 xPos = 0;
184 yPos += cHeight;
185 cHeight = 0;
186 }
187
188 Node node;
189 node.k.bg = bg;
190 node.k.fg = fg;
191 node.k.ch = ch;
192
193 if ( image.Width() - xPos >= charW && image.Height() - yPos >= charH )
194 {
195 node.place.Set( xPos, yPos, charW, charH );
196 xPos += charW;
197
198 if ( cHeight < charH ) { cHeight = charH; }
199
200 return &(nodesHash[ node.k ] = node);
201 }
202
203 nodesHash.clear();
204
205 int w = image.Width();
206 int h = image.Height();
207
208 w = ( w ? ( w * 2 ) : ( 16 * charW ) );
209 h = ( h ? ( h * 2 ) : charH * 2 );
210
211 if ( w > MAX_IMAGE_WIDTH ) { w = MAX_IMAGE_WIDTH; }
212
213 if ( h > MAX_IMAGE_HEIGHT ) { h = MAX_IMAGE_HEIGHT; }
214
215 if ( image.Width() != w || image.Height() != h )
216 {
217 image.Create( w, h );
218 }
219
220 node.place.Set( 0, 0, charW, charH );
221
222 xPos = charW;
223 yPos = 0;
224 cHeight = charH;
225
226 return &(nodesHash[ node.k ] = node);
227 }
228
DrawImage(wal::GC & gc,Image32 & im,int x,int y)229 inline void ImCache::DrawImage( wal::GC& gc, Image32& im, int x, int y )
230 {
231 IntXImage ximage( im );
232 ximage.Put( gc, 0, 0, x, y, im.width(), im.height() );
233 }
234
235
Draw(wal::GC & gc,int x,int y,unicode_t ch,unsigned bg,unsigned fg,Image32 & im)236 void ImCache::Draw( wal::GC& gc, int x, int y, unicode_t ch, unsigned bg, unsigned fg, Image32& im )
237 {
238 Node* node = Alloc( ch, bg, fg, im.width(), im.height() );
239
240 if ( node )
241 {
242 wal::GC imageGC( &image );
243 imageGC.SetFillColor( bg );
244 DrawImage( imageGC, im, node->place.x, node->place.y );
245 CopyArea( gc, node, x, y );
246 return;
247 }
248
249 DrawImage( gc, im, x, y );
250 return;
251 }
252
~ImCache()253 ImCache::~ImCache() {}
254
255
256 ////////////////////// FFace ///////////////////////////////////////////////////////////
257
258
FFace()259 FFace::FFace(): face( 0 ), pxAscender( 0 ), pxHeight( 0 ), _size( 1 ) {}
~FFace()260 FFace::~FFace() { if ( face ) { FT_Done_Face( face ); } }
261
Load(const char * fileName,int index,int size,int xRes,int yRes)262 int FFace::Load( const char* fileName, int index, int size, int xRes, int yRes )
263 {
264 MutexLock lock( &mutex );
265
266 if ( !CheckLib() ) { return 1; }
267
268 Clear();
269 int e = FT_New_Face( library, fileName, index, &face );
270
271 if ( e ) { return e; }
272
273 e = SetSize( size, xRes, yRes );
274
275 if ( e ) { Clear(); return e; }
276
277 return 0;
278 }
279
SetSize(int size,int xRes,int yRes)280 int FFace::SetSize( int size, int xRes, int yRes )
281 {
282 imCache.Clear();
283 _size = 1;
284 int e = FT_Set_Char_Size(
285 face, /* handle to face object */
286 0, /* char_width in 1/64th of points */
287 size, /* char_height in 1/64th of points */
288 xRes, /* horizontal device resolution */
289 yRes ) ; /* vertical device resolution */
290
291 if ( !e && face && face->size )
292 {
293 pxAscender = face->size->metrics.ascender >> 6;
294 pxHeight = ( face->size->metrics.height + 0x3F ) >> 6;
295
296 }
297 else
298 {
299 pxAscender = 0;
300 pxHeight = 1;
301 }
302
303 if ( !e ) { _size = size; }
304
305 return e;
306 }
307
308 //bg(1-a)+fg*a
fff(int bg,int fg,int a,int a1)309 inline unsigned fff( int bg, int fg, int a, int a1 )
310 {
311 return ( ( ( bg & 0xFF ) * a1 + ( fg & 0xFF ) * a ) / 256 ) & 0xFF;
312 }
313
MkImage(Image32 & im,FT_GlyphSlot slot)314 bool FFace::MkImage( Image32& im, FT_GlyphSlot slot )
315 {
316 if ( !slot->bitmap.buffer ) { return false; }
317
318
319 int h = pxHeight;
320 int w = ( ( slot->metrics.horiAdvance + 0x3F ) >> 6 );
321
322
323 if ( w <= 0 || h <= 0 ) { return false; }
324
325 im.alloc( w, h );
326
327 unsigned bg = cData.bg;
328 unsigned fg = cData.fg;
329 cData.Prepare();
330
331 int left = slot->bitmap_left;
332 int top = pxAscender - slot->bitmap_top - 1;
333
334 if ( top < 0 ) { top = 0; }
335
336 int bottom = top + slot->bitmap.rows;
337 int right = left + slot->bitmap.width;
338
339 if ( top >= h || bottom <= 0 || left >= w || right <= 0 )
340 {
341 uint32_t* p = im.line( 0 );
342
343 for ( int cnt = w * h; cnt > 0; cnt--, p++ )
344 {
345 *p = bg;
346 }
347
348 return true;
349 }
350
351 int leftBgCount = ( left > 0 ) ? ( left > w ? w : left ) : 0;
352 int rightBgCount = ( right < w ) ? w - right : 0;
353 int bitmapOffset = left < 0 ? -left : 0;
354
355 if ( left < 0 ) { left = 0; }
356
357 if ( right > w ) { right = w; }
358
359 int bitmapN = right - left;
360
361
362
363
364 for ( int i = 0; i < h; i++ )
365 {
366 uint32_t* dest = im.line( i );
367
368 if ( i < top || i >= bottom )
369 {
370 for ( int cnt = w; cnt > 0; cnt--, dest++ ) { *dest = bg; }
371 }
372 else
373 {
374 int n;
375
376 for ( n = leftBgCount ; n > 0; n--, dest++ ) { *dest = bg; }
377
378 unsigned char* s = slot->bitmap.buffer + ( i - top ) * slot->bitmap.width + bitmapOffset;
379
380 for ( n = bitmapN; n > 0; n--, dest++, s++ )
381 {
382 unsigned c = *s;
383
384 unsigned color;
385
386 if ( c >= 0xFF )
387 {
388 color = fg;
389 }
390 else if ( c <= 0 )
391 {
392 color = bg;
393 }
394 else if ( cData.IsSet( c ) )
395 {
396 color = cData.Get( c );
397 }
398 else
399 {
400 // bg(1-a)+fg*a
401
402 unsigned c1 = 255 - c;
403
404 /*
405 color = fff(bg, fg , c, c1) +
406 (fff(bg>>8, fg>>8, c, c1)<<8)+
407 (fff(bg>>16, fg>>16, c, c1)<<16);
408 */
409
410
411 color =
412 ( ( ( ( bg & 0x0000FF ) * c1 + ( fg & 0x0000FF ) * c ) >> 8 ) & 0x0000FF ) +
413 ( ( ( ( bg & 0x00FF00 ) * c1 + ( fg & 0x00FF00 ) * c ) >> 8 ) & 0x00FF00 ) +
414 ( ( ( ( bg & 0xFF0000 ) * c1 + ( fg & 0xFF0000 ) * c ) >> 8 ) & 0xFF0000 );
415
416
417
418 cData.Set( c, color );
419 };
420
421 *dest = color;
422 }
423
424 for ( n = rightBgCount ; n > 0; n--, dest++ ) { *dest = bg; }
425 }
426 }
427
428 return true;
429 }
430
431
OutTextF(wal::GC & gc,int x,int y,const unicode_t * text,int count)432 void FFace::OutTextF( wal::GC& gc, int x, int y, const unicode_t* text, int count )
433 {
434 MutexLock lock( &mutex );
435
436 if ( !CheckLib() ) { return; }
437
438 if ( !face ) { return; }
439
440 if ( count < 0 ) { count = unicode_strlen( text ); }
441
442 for ( int i = 0; i < count; i++ )
443 {
444 OutCharF( gc, &x, &y, text[i] );
445 }
446 }
447
OutText(wal::GC & gc,int x,int y,const unicode_t * text,int count)448 void FFace::OutText( wal::GC& gc, int x, int y, const unicode_t* text, int count )
449 {
450 MutexLock lock( &mutex );
451
452 if ( !CheckLib() ) { return; }
453
454 if ( !face ) { return; }
455
456 if ( count < 0 ) { count = unicode_strlen( text ); }
457
458 for ( int i = 0; i < count; i++ )
459 {
460 OutChar( gc, &x, &y, text[i] );
461 }
462 }
463
GetSlot(FT_Face face,unicode_t * pc)464 inline FT_GlyphSlot GetSlot( FT_Face face, unicode_t* pc )
465 {
466 if ( !face ) { return 0; }
467
468 FT_UInt glyph_index = FT_Get_Char_Index( face, *pc );
469
470 if ( !glyph_index )
471 {
472 glyph_index = FT_Get_Char_Index( face, '.' );
473
474 if ( !glyph_index ) { return 0; }
475
476 *pc = '.';
477 }
478
479 if ( FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT /*| FT_LOAD_TARGET_LIGHT*/ ) ) { return 0; }
480
481 if ( FT_Render_Glyph( face->glyph, /*FT_RENDER_MODE_LCD*/ FT_RENDER_MODE_NORMAL ) ) { return 0; }
482
483 return face->glyph;
484 }
485
486
GetTextExtents(const unicode_t * text,int count)487 cpoint FFace::GetTextExtents( const unicode_t* text, int count )
488 {
489 MutexLock lock( &mutex );
490
491 if ( !CheckLib() ) { return cpoint( 0, 0 ); }
492
493 if ( !face ) { return cpoint( 0, 0 ); }
494
495 if ( count < 0 ) { count = unicode_strlen( text ); }
496
497 int w = 0;
498
499 for ( int i = 0; i < count; i++, text++ )
500 {
501 auto iter = ciHash.find( *text );
502
503 CharInfo* pInfo = ( iter == ciHash.end() ) ? nullptr : &( iter->second );
504
505 if ( !pInfo )
506 {
507 unicode_t c = *text;
508 FT_GlyphSlot slot = GetSlot( face, &c );
509
510 if ( !slot ) { continue; }
511
512 CharInfo info( slot );
513 ciHash[c] = info;
514 pInfo = &( ciHash[c] );
515 }
516
517 w += pInfo->pxWidth;
518 };
519
520 return cpoint( w, PxHeight() );
521 }
522
OutCharF(wal::GC & gc,int * px,int * py,unicode_t c)523 void FFace::OutCharF( wal::GC& gc, int* px, int* py, unicode_t c )
524 {
525 int bg = gc.FillRgb();
526 int fg = gc.TextRgb();
527
528 auto i = ciHash.find( c );
529
530 CharInfo* pInfo = ( i == ciHash.end() ) ? nullptr : &( i->second );
531
532 if ( pInfo )
533 {
534 if ( imCache.DrawIfExist( gc, *px, *py , c, bg, fg ) )
535 {
536 *px += pInfo->pxWidth;
537 return;
538 }
539 };
540
541 FT_GlyphSlot slot = GetSlot( face, &c );
542
543 if ( !slot ) { return; }
544
545 if ( !pInfo )
546 {
547 CharInfo info( slot );
548 pInfo = &( ciHash[c] = info );
549 }
550
551 Image32 im;
552 cData.SetBg( bg );
553 cData.SetFg( fg );
554
555 if ( !MkImage( im, slot ) )
556 {
557 gc.FillRect( crect( *px, *py, *px + pInfo->pxWidth, *py + pxHeight ) );
558 *px += pInfo->pxWidth;
559 return;
560 }
561
562 imCache.Draw( gc, *px, *py, c, bg, fg, im );
563 *px += pInfo->pxWidth;
564 }
565
566
OutChar(wal::GC & gc,int * px,int * py,unicode_t c)567 void FFace::OutChar( wal::GC& gc, int* px, int* py, unicode_t c )
568 {
569 FT_GlyphSlot slot = GetSlot( face, &c );
570
571 if ( !slot ) { return; }
572
573 int x = *px;
574 int y = *py;
575
576 auto i = ciHash.find( c );
577
578 CharInfo* pInfo = ( i == ciHash.end() ) ? nullptr : &( i->second );
579
580 if ( !pInfo )
581 {
582 CharInfo info( slot );
583 pInfo = &( ciHash[c] = info );
584 }
585
586 *px += pInfo->pxWidth;
587
588 if ( !slot->bitmap.buffer )
589 {
590 return;
591 }
592
593 int h = slot->bitmap.rows;
594 int w = slot->bitmap.width;
595 unsigned char* p = slot->bitmap.buffer;
596
597 int fg = gc.TextRgb();
598
599 int left = slot->bitmap_left;
600 int top = pxAscender - slot->bitmap_top - 1;
601
602 if ( top < 0 ) { top = 0; }
603
604 for ( int i = 0; i < h; i++ )
605 for ( int j = 0; j < w; j++, p++ )
606 {
607 unsigned c = *p;
608
609 if ( c >= 0x80 )
610 {
611 gc.SetPixel( x + j + left, y + i + top, fg );
612 }
613 }
614 }
615
616
617 }; //namespace FTU
618
GetFTFileInfo(const char * path)619 clPtr<cfont::FTInfo> cfont::GetFTFileInfo( const char* path )
620 {
621 FTU::FFace f;
622
623 if ( f.Load( path, 0, 100 ) ) { return 0; }
624
625 clPtr<cfont::FTInfo> node = new cfont::FTInfo;
626 node->name = f.Name();
627 node->styleName = f.StyleName();
628
629 node->flags = 0;
630
631 if ( f.FaceFlags() & FT_FACE_FLAG_FIXED_WIDTH ) { node->flags |= cfont::FTInfo::FIXED_WIDTH; }
632
633
634 return node;
635 }
636
637 #endif
638