1 #include <lib/gdi/font.h>
10 // use this for init Freetype...
12 #include FT_FREETYPE_H
14 #include <lib/base/eerror.h>
15 #include <lib/gdi/lcd.h>
16 #include <lib/gdi/grc.h>
17 #include <lib/system/elock.h>
18 #include <lib/system/init.h>
19 #include <lib/system/init_num.h>
21 //#define HAVE_FRIBIDI
22 // until we have it in the cdk
25 #include <fribidi/fribidi.h>
30 fontRenderClass
*fontRenderClass::instance
;
32 static pthread_mutex_t ftlock
=
33 PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
; // RECURSIVE
35 static FTC_Font cache_current_font
=0;
37 struct fntColorCacheKey
40 fntColorCacheKey(const gRGB
&start
, const gRGB
&end
)
41 : start(start
), end(end
)
44 bool operator <(const fntColorCacheKey
&c
) const
48 else if (start
== c
.start
)
54 std::map
<fntColorCacheKey
,gLookup
> colorcache
;
56 static gLookup
&getColor(const gPalette
&pal
, const gRGB
&start
, const gRGB
&end
)
58 fntColorCacheKey
key(start
, end
);
59 std::map
<fntColorCacheKey
,gLookup
>::iterator i
=colorcache
.find(key
);
60 if (i
!= colorcache
.end())
62 gLookup
&n
=colorcache
.insert(std::pair
<fntColorCacheKey
,gLookup
>(key
,gLookup())).first
->second
;
63 eDebug("[FONT] creating new font color cache entry %02x%02x%02x%02x .. %02x%02x%02x%02x", start
.a
, start
.r
, start
.g
, start
.b
,
64 end
.a
, end
.r
, end
.g
, end
.b
);
65 n
.build(16, pal
, start
, end
);
66 /* for (int i=0; i<16; i++)
67 eDebugNoNewLine("%02x|%02x%02x%02x%02x ", (int)n.lookup[i], pal.data[n.lookup[i]].a, pal.data[n.lookup[i]].r, pal.data[n.lookup[i]].g, pal.data[n.lookup[i]].b);
72 fontRenderClass
*fontRenderClass::getInstance()
77 FT_Error
myFTC_Face_Requester(FTC_FaceID face_id
,
79 FT_Pointer request_data
,
82 return ((fontRenderClass
*)request_data
)->FTC_Face_Requester(face_id
, aface
);
86 FT_Error
fontRenderClass::FTC_Face_Requester(FTC_FaceID face_id
, FT_Face
* aface
)
88 fontListEntry
*font
=(fontListEntry
*)face_id
;
93 // eDebug("[FONT] FTC_Face_Requester (%s)", font->face.c_str());
95 if ((error
=FT_New_Face(library
, font
->filename
.c_str(), 0, aface
)))
97 eDebug(" failed: %s", strerror(error
));
100 FT_Select_Charmap(*aface
, ft_encoding_unicode
);
104 FTC_FaceID
fontRenderClass::getFaceID(const eString
&face
)
106 for (fontListEntry
*f
=font
; f
; f
=f
->next
)
109 return (FTC_FaceID
)f
;
114 FT_Error
fontRenderClass::getGlyphBitmap(FTC_Image_Desc
*font
, FT_ULong glyph_index
, FTC_SBit
*sbit
)
116 FT_Error res
=FTC_SBit_Cache_Lookup(sbitsCache
, font
, glyph_index
, sbit
);
120 eString
fontRenderClass::AddFont(const eString
&filename
, const eString
&name
, int scale
)
122 eDebugNoNewLine("[FONT] adding font %s...", filename
.c_str());
124 fontListEntry
*n
=new fontListEntry
;
128 singleLock
s(ftlock
);
130 if ((error
=FT_New_Face(library
, filename
.c_str(), 0, &face
)))
131 eFatal(" failed: %s", strerror(error
));
133 n
->filename
=filename
;
138 eDebug("OK (%s)", n
->face
.c_str());
144 fontRenderClass::fontListEntry::~fontListEntry()
148 fontRenderClass::fontRenderClass()
149 :fb(fbClass::getInstance())
152 eDebug("[FONT] initializing lib...");
154 if (FT_Init_FreeType(&library
))
156 eDebug("[FONT] initializing failed.");
160 eDebug("[FONT] loading fonts...");
162 int maxbytes
=4*1024*1024;
163 eDebug("[FONT] Intializing font cache, using max. %dMB...", maxbytes
/1024/1024);
165 if (FTC_Manager_New(library
, 8, 8, maxbytes
, myFTC_Face_Requester
, this, &cacheManager
))
167 eDebug("[FONT] initializing font cache failed!");
173 eDebug("[FONT] initializing font cache manager error.");
177 if (FTC_SBit_Cache_New(cacheManager
, &sbitsCache
))
179 eDebug("[FONT] initializing font cache sbit failed!");
183 if (FTC_Image_Cache_New(cacheManager
, &imageCache
))
184 eDebug("[FONT] initializing font cache imagecache failed!");
187 float fontRenderClass::getLineHeight(const gFont
& font
)
191 Font
*fnt
= getFont( font
.family
.c_str(), font
.pointSize
);
194 singleLock
s(ftlock
);
195 FT_Face current_face
;
196 if (FTC_Manager_Lookup_Size(cacheManager
, &fnt
->font
.font
, ¤t_face
, &fnt
->size
)<0)
199 eDebug("FTC_Manager_Lookup_Size failed!");
202 int linegap
=current_face
->size
->metrics
.height
-(current_face
->size
->metrics
.ascender
+current_face
->size
->metrics
.descender
);
203 float height
=(current_face
->size
->metrics
.ascender
+current_face
->size
->metrics
.descender
+linegap
/2.0)/64;
209 fontRenderClass::~fontRenderClass()
211 singleLock
s(ftlock
);
214 fontListEntry
*f
=font
;
218 // auskommentiert weil freetype und enigma die kritische masse des suckens ueberschreiten.
219 // FTC_Manager_Done(cacheManager);
220 // FT_Done_FreeType(library);
223 Font
*fontRenderClass::getFont(const eString
&face
, int size
, int tabwidth
)
225 FTC_FaceID id
=getFaceID(face
);
228 return new Font(this, id
, size
* ((fontListEntry
*)id
)->scale
/ 100, tabwidth
);
231 Font::Font(fontRenderClass
*render
, FTC_FaceID faceid
, int isize
, int tw
)
232 :renderer(render
), ref(0), tabwidth(tw
), height(isize
)
234 font
.font
.face_id
=faceid
;
235 font
.font
.pix_width
= isize
;
236 font
.font
.pix_height
= isize
;
237 font
.image_type
= ftc_image_grays
;
240 // font.image_type |= ftc_image_flag_autohinted;
243 FT_Error
Font::getGlyphBitmap(FT_ULong glyph_index
, FTC_SBit
*sbit
)
245 return renderer
->getGlyphBitmap(&font
, glyph_index
, sbit
);
264 int eTextPara::appendGlyph(Font
*current_font
, FT_Face current_face
, FT_UInt glyphIndex
, int flags
, int rflags
)
267 if (current_font
->getGlyphBitmap(glyphIndex
, &glyph
))
280 glyphString::reverse_iterator
i(glyphs
.rbegin());
281 while (i
!= glyphs
.rend())
283 if (i
->flags
&(GS_ISSPACE
|GS_ISFIRST
))
288 if (i
!= glyphs
.rend()
289 && ((i
->flags
&(GS_ISSPACE
|GS_ISFIRST
))==GS_ISSPACE
)
293 int linelength
=cursor
.x()-i
->x
;
294 i
->flags
|=GS_ISFIRST
;
295 ePoint offset
=ePoint(i
->x
, i
->y
);
302 i
->bbox
.moveBy(-offset
.x(), -offset
.y());
304 while (i
-- != glyphs
.rbegin()); // rearrange them into the next line
305 cursor
+=ePoint(linelength
, 0); // put the cursor after that line
317 int xadvance
=glyph
->xadvance
, kern
=0;
319 if (previous
&& use_kerning
)
322 FT_Get_Kerning(current_face
, previous
, glyphIndex
, ft_kerning_default
, &delta
);
327 ng
.bbox
.setLeft( (flags
&GS_ISFIRST
|cursor
.x()-1)+glyph
->left
);
328 ng
.bbox
.setTop( cursor
.y() - glyph
->top
);
329 ng
.bbox
.setWidth( glyph
->width
);
330 ng
.bbox
.setHeight( glyph
->height
);
334 ng
.x
=cursor
.x()+kern
;
337 ng
.font
=current_font
;
339 ng
.glyph_index
=glyphIndex
;
342 glyphs
.push_back(ng
);
344 cursor
+=ePoint(xadvance
, 0);
349 void eTextPara::calc_bbox()
351 boundBox
.setLeft( 32000 );
352 boundBox
.setTop( 32000 );
353 boundBox
.setRight( -32000 ); // for each glyph image, compute its bounding box, translate it,
354 boundBox
.setBottom( -32000 );
355 // and grow the string bbox
357 for ( glyphString::iterator
i(glyphs
.begin()); i
!= glyphs
.end(); ++i
)
359 if ( i
->flags
& GS_ISSPACE
)
361 if ( i
->bbox
.left() < boundBox
.left() )
362 boundBox
.setLeft( i
->bbox
.left() );
363 if ( i
->bbox
.top() < boundBox
.top() )
364 boundBox
.setTop( i
->bbox
.top() );
365 if ( i
->bbox
.right() > boundBox
.right() )
366 boundBox
.setRight( i
->bbox
.right() );
367 if ( i
->bbox
.bottom() > boundBox
.bottom() )
368 boundBox
.setBottom( i
->bbox
.bottom() );
370 // eDebug("boundBox left = %i, top = %i, right = %i, bottom = %i", boundBox.left(), boundBox.top(), boundBox.right(), boundBox.bottom() );
375 void eTextPara::newLine(int flags
)
377 if (maximum
.width()<cursor
.x())
378 maximum
.setWidth(cursor
.x());
381 int linegap
=current_face
->size
->metrics
.height
-(current_face
->size
->metrics
.ascender
+current_face
->size
->metrics
.descender
);
382 cursor
+=ePoint(0, (current_face
->size
->metrics
.ascender
+current_face
->size
->metrics
.descender
+linegap
*1/2)>>6);
383 if (maximum
.height()<cursor
.y())
384 maximum
.setHeight(cursor
.y());
388 eTextPara::~eTextPara()
392 eFatal("verdammt man der war noch gelockt :/\n");
395 void eTextPara::destroy()
401 eTextPara
*eTextPara::grab()
407 void eTextPara::setFont(const gFont
&font
)
410 eFatal("mod. after lock");
412 Font
*fnt
=fontRenderClass::getInstance()->getFont(font
.family
.c_str(), font
.pointSize
);
414 eWarning("FONT '%s' MISSING!", font
.family
.c_str());
416 fontRenderClass::getInstance()->getFont(replacement_facename
.c_str(), font
.pointSize
));
419 eString
eTextPara::replacement_facename
;
421 void eTextPara::setFont(Font
*fnt
, Font
*replacement
)
424 eFatal("mod. after lock");
431 if ( !current_font
->ref
)
435 if (replacement_font
)
437 if (!replacement_font
->ref
)
438 delete replacement_font
;
440 replacement_font
=replacement
;
441 singleLock
s(ftlock
);
443 // we ask for replacment_font first becauseof the cache
444 if (replacement_font
)
446 if (FTC_Manager_Lookup_Size(fontRenderClass::instance
->cacheManager
,
447 &replacement_font
->font
.font
, &replacement_face
,
448 &replacement_font
->size
)<0)
450 eDebug("FTC_Manager_Lookup_Size failed!");
456 if (FTC_Manager_Lookup_Size(fontRenderClass::instance
->cacheManager
, ¤t_font
->font
.font
, ¤t_face
, ¤t_font
->size
)<0)
458 eDebug("FTC_Manager_Lookup_Size failed!");
462 cache_current_font
=¤t_font
->font
.font
;
464 use_kerning
=FT_HAS_KERNING(current_face
);
468 shape (std::vector
<unsigned long> &string
, const std::vector
<unsigned long> &text
);
470 int eTextPara::renderString(const eString
&string
, int rflags
)
472 singleLock
s(ftlock
);
475 eFatal("mod. after lock");
482 cursor
=ePoint(area
.x(), area
.y()+(current_face
->size
->metrics
.ascender
>>6));
486 if (¤t_font
->font
.font
!= cache_current_font
)
488 if (FTC_Manager_Lookup_Size(fontRenderClass::instance
->cacheManager
, ¤t_font
->font
.font
, ¤t_face
, ¤t_font
->size
)<0)
490 eDebug("FTC_Manager_Lookup_Size failed!");
493 cache_current_font
=¤t_font
->font
.font
;
496 std::vector
<unsigned long> uc_string
, uc_visual
;
497 uc_string
.reserve(string
.length());
499 eString::const_iterator
p(string
.begin());
501 while(p
!= string
.end())
503 unsigned int unicode
=*p
++;
505 if (unicode
& 0x80) // we have (hopefully) UTF8 here, and we assume that the encoding is VALID
507 if ((unicode
& 0xE0)==0xC0) // two bytes
511 if (p
!= string
.end())
512 unicode
|=(*p
++)&0x3F;
513 } else if ((unicode
& 0xF0)==0xE0) // three bytes
517 if (p
!= string
.end())
518 unicode
|=(*p
++)&0x3F;
520 if (p
!= string
.end())
521 unicode
|=(*p
++)&0x3F;
522 } else if ((unicode
& 0xF8)==0xF0) // four bytes
526 if (p
!= string
.end())
527 unicode
|=(*p
++)&0x3F;
529 if (p
!= string
.end())
530 unicode
|=(*p
++)&0x3F;
532 if (p
!= string
.end())
533 unicode
|=(*p
++)&0x3F;
536 uc_string
.push_back(unicode
);
539 std::vector
<unsigned long> uc_shape
;
541 // character -> glyph conversion
542 shape(uc_shape
, uc_string
);
544 // now do the usual logical->visual reordering
546 FriBidiCharType dir
=FRIBIDI_TYPE_ON
;
548 int size
=uc_shape
.size();
549 uc_visual
.resize(size
);
550 // gaaanz lahm, aber anders geht das leider nicht, sorry.
551 FriBidiChar array
[size
], target
[size
];
552 std::copy(uc_shape
.begin(), uc_shape
.end(), array
);
553 fribidi_log2vis(array
, size
, &dir
, target
, 0, 0, 0);
554 uc_visual
.assign(target
, target
+size
);
560 glyphs
.reserve(uc_visual
.size());
564 for (std::vector
<unsigned long>::const_iterator
i(uc_visual
.begin());
565 i
!= uc_visual
.end(); ++i
)
568 int flags
= nextflags
;
570 if (!(rflags
&RS_DIRECT
))
576 unsigned long c
= *(i
+1);
595 cursor
+=ePoint(current_font
->tabwidth
, 0);
596 cursor
-=ePoint(cursor
.x()%current_font
->tabwidth
, 0);
601 newline
:isprintable
=0;
603 nextflags
|=GS_ISFIRST
;
606 case 0x86: case 0xE086:
607 case 0x87: case 0xE087:
608 nprint
: isprintable
=0;
620 index
=(rflags
&RS_DIRECT
)? *i
: FT_Get_Char_Index(current_face
, *i
);
624 if (replacement_face
)
625 index
=(rflags
&RS_DIRECT
)? *i
: FT_Get_Char_Index(replacement_face
, *i
);
628 eDebug("unicode %d ('%c') not present", *i
, *i
);
630 appendGlyph(replacement_font
, replacement_face
, index
, flags
, rflags
);
632 appendGlyph(current_font
, current_face
, index
, flags
, rflags
);
638 if (dir
& FRIBIDI_MASK_RTL
)
644 void eTextPara::blit(gPixmapDC
&dc
, const ePoint
&offset
, const gRGB
&background
, const gRGB
&foreground
)
646 singleLock
s(ftlock
);
651 if (¤t_font
->font
.font
!= cache_current_font
)
653 if (FTC_Manager_Lookup_Size(fontRenderClass::instance
->cacheManager
, ¤t_font
->font
.font
, ¤t_face
, ¤t_font
->size
)<0)
655 eDebug("FTC_Manager_Lookup_Size failed!");
658 cache_current_font
=¤t_font
->font
.font
;
661 gPixmap
&target
=dc
.getPixmap();
669 if (target
.clut
.data
)
671 lookup8
=getColor(target
.clut
, background
, foreground
).lookup
;
675 } else if (target
.bpp
== 32)
678 if (target
.clut
.data
)
680 lookup8
=getColor(target
.clut
, background
, foreground
).lookup
;
681 for (int i
=0; i
<16; ++i
)
682 lookup32
[i
]=((target
.clut
.data
[lookup8
[i
]].a
<<24)|
683 (target
.clut
.data
[lookup8
[i
]].r
<<16)|
684 (target
.clut
.data
[lookup8
[i
]].g
<<8)|
685 (target
.clut
.data
[lookup8
[i
]].b
))^0xFF000000;
688 for (int i
=0; i
<16; ++i
)
689 lookup32
[i
]=(0x010101*i
)|0xFF000000;
693 eWarning("can't render to %dbpp", target
.bpp
);
697 eRect
clip(0, 0, target
.x
, target
.y
);
700 int buffer_stride
=target
.stride
;
702 for (glyphString::iterator
i(glyphs
.begin()); i
!= glyphs
.end(); ++i
)
704 static FTC_SBit glyph_bitmap
;
705 if (fontRenderClass::instance
->getGlyphBitmap(&i
->font
->font
, i
->glyph_index
, &glyph_bitmap
))
707 int rx
=i
->x
+glyph_bitmap
->left
+ offset
.x();
708 int ry
=i
->y
-glyph_bitmap
->top
+ offset
.y();
709 __u8
*d
=(__u8
*)(target
.data
)+buffer_stride
*ry
+rx
*target
.bypp
;
710 __u8
*s
=glyph_bitmap
->buffer
;
711 register int sx
=glyph_bitmap
->width
;
712 int sy
=glyph_bitmap
->height
;
713 if ((sy
+ry
) >= clip
.bottom())
715 if ((sx
+rx
) >= clip
.right())
717 if (rx
< clip
.left())
719 int diff
=clip
.left()-rx
;
727 int diff
=clip
.top()-ry
;
728 s
+=diff
*glyph_bitmap
->pitch
;
731 d
+=diff
*buffer_stride
;
734 for (int ay
=0; ay
<sy
; ay
++)
736 if (!opcode
) // 4bit lookup to 8bit
740 for (ax
=0; ax
<sx
; ax
++)
742 register int b
=(*s
++)>>4;
748 } else if (opcode
== 1) // 8bit direct
752 for (ax
=0; ax
<sx
; ax
++)
759 register __u32
*td
=(__u32
*)d
;
761 for (ax
=0; ax
<sx
; ax
++)
763 register int b
=(*s
++)>>4;
770 s
+=glyph_bitmap
->pitch
-sx
;
776 void eTextPara::realign(int dir
) // der code hier ist ein wenig merkwuerdig.
779 eFatal("mod. after lock");
784 glyphString::iterator
begin(glyphs
.begin()), c(glyphs
.begin()), end(glyphs
.begin()), last
;
785 while (c
!= glyphs
.end())
788 int numspaces
=0, num
=0;
791 ASSERT( end
!= glyphs
.end());
797 } while ((end
!= glyphs
.end()) && (!(end
->flags
&GS_ISFIRST
)));
798 // end zeigt jetzt auf begin der naechsten zeile
800 for (c
=begin
; c
!=end
; ++c
)
802 // space am zeilenende skippen
803 if ((c
==last
) && (c
->flags
&GS_ISSPACE
))
806 if (c
->flags
&GS_ISSPACE
)
812 if (!num
) // line mit nur einem space
820 int offset
=area
.width()-linelength
;
826 begin
->bbox
.moveBy(offset
-begin
->x
,0);
835 if (end
== glyphs
.end()) // letzte zeile linksbuendig lassen
842 if ((!spacemode
) && (num
<2))
844 int off
=(area
.width()-linelength
)*256/(spacemode
?numspaces
:(num
-1));
849 if ((!spacemode
) || (begin
->flags
&GS_ISSPACE
))
852 begin
->bbox
.moveBy(curoff
>>8,0);
865 void eTextPara::clear()
867 singleLock
s(ftlock
);
869 if ( current_font
&& !current_font
->ref
)
872 if ( replacement_font
&& !replacement_font
->ref
)
873 delete replacement_font
;
875 for (glyphString::iterator
i(glyphs
.begin()); i
!=glyphs
.end(); ++i
)
880 eAutoInitP0
<fontRenderClass
> init_fontRenderClass(eAutoInitNumbers::graphic
-1, "Font Render Class");