trunk 20080912
[gitenigma.git] / lib / gdi / font.cpp
blob033bd41795ba5b8a3fec98fb40219fe313a0681b
1 #include <lib/gdi/font.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <ctype.h>
6 #include <pthread.h>
7 #include <sys/types.h>
8 #include <unistd.h>
10 // use this for init Freetype...
11 #include <ft2build.h>
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
24 #ifdef HAVE_FRIBIDI
25 #include <fribidi/fribidi.h>
26 #endif
28 #include <map>
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
39 gRGB start, end;
40 fntColorCacheKey(const gRGB &start, const gRGB &end)
41 : start(start), end(end)
44 bool operator <(const fntColorCacheKey &c) const
46 if (start < c.start)
47 return 1;
48 else if (start == c.start)
49 return end < c.end;
50 return 0;
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())
61 return i->second;
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);
68 eDebug("");*/
69 return n;
72 fontRenderClass *fontRenderClass::getInstance()
74 return instance;
77 FT_Error myFTC_Face_Requester(FTC_FaceID face_id,
78 FT_Library library,
79 FT_Pointer request_data,
80 FT_Face* aface)
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;
90 if (!font)
91 return -1;
93 // eDebug("[FONT] FTC_Face_Requester (%s)", font->face.c_str());
94 int error;
95 if ((error=FT_New_Face(library, font->filename.c_str(), 0, aface)))
97 eDebug(" failed: %s", strerror(error));
98 return error;
100 FT_Select_Charmap(*aface, ft_encoding_unicode);
101 return 0;
104 FTC_FaceID fontRenderClass::getFaceID(const eString &face)
106 for (fontListEntry *f=font; f; f=f->next)
108 if (f->face == face)
109 return (FTC_FaceID)f;
111 return 0;
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);
117 return res;
120 eString fontRenderClass::AddFont(const eString &filename, const eString &name, int scale)
122 eDebugNoNewLine("[FONT] adding font %s...", filename.c_str());
123 int error;
124 fontListEntry *n=new fontListEntry;
126 n->scale=scale;
127 FT_Face face;
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;
134 n->face=name;
135 FT_Done_Face(face);
137 n->next=font;
138 eDebug("OK (%s)", n->face.c_str());
139 font=n;
141 return n->face;
144 fontRenderClass::fontListEntry::~fontListEntry()
148 fontRenderClass::fontRenderClass()
149 :fb(fbClass::getInstance())
151 instance=this;
152 eDebug("[FONT] initializing lib...");
154 if (FT_Init_FreeType(&library))
156 eDebug("[FONT] initializing failed.");
157 return;
160 eDebug("[FONT] loading fonts...");
161 font=0;
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!");
168 return;
171 if (!cacheManager)
173 eDebug("[FONT] initializing font cache manager error.");
174 return;
177 if (FTC_SBit_Cache_New(cacheManager, &sbitsCache))
179 eDebug("[FONT] initializing font cache sbit failed!");
180 return;
183 if (FTC_Image_Cache_New(cacheManager, &imageCache))
184 eDebug("[FONT] initializing font cache imagecache failed!");
187 float fontRenderClass::getLineHeight(const gFont& font)
189 if (!instance)
190 return 0;
191 Font *fnt = getFont( font.family.c_str(), font.pointSize);
192 if (!fnt)
193 return 0;
194 singleLock s(ftlock);
195 FT_Face current_face;
196 if (FTC_Manager_Lookup_Size(cacheManager, &fnt->font.font, &current_face, &fnt->size)<0)
198 delete fnt;
199 eDebug("FTC_Manager_Lookup_Size failed!");
200 return 0;
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;
204 delete fnt;
205 return height;
209 fontRenderClass::~fontRenderClass()
211 singleLock s(ftlock);
212 while(font)
214 fontListEntry *f=font;
215 font=font->next;
216 delete f;
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);
226 if (!id)
227 return 0;
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;
238 if (tabwidth==-1)
239 tabwidth=8*isize;
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);
248 Font::~Font()
252 void Font::lock()
254 ref++;
257 void Font::unlock()
259 ref--;
260 if (!ref)
261 delete this;
264 int eTextPara::appendGlyph(Font *current_font, FT_Face current_face, FT_UInt glyphIndex, int flags, int rflags)
266 FTC_SBit glyph;
267 if (current_font->getGlyphBitmap(glyphIndex, &glyph))
268 return 1;
270 int nx=cursor.x();
272 nx+=glyph->xadvance;
274 if (
275 (rflags&RS_WRAP) &&
276 (nx >= area.right())
279 int cnt = 0;
280 glyphString::reverse_iterator i(glyphs.rbegin());
281 while (i != glyphs.rend())
283 if (i->flags&(GS_ISSPACE|GS_ISFIRST))
284 break;
285 cnt++;
286 ++i;
288 if (i != glyphs.rend()
289 && ((i->flags&(GS_ISSPACE|GS_ISFIRST))==GS_ISSPACE)
290 && cnt )
292 --i;
293 int linelength=cursor.x()-i->x;
294 i->flags|=GS_ISFIRST;
295 ePoint offset=ePoint(i->x, i->y);
296 newLine(rflags);
297 offset-=cursor;
300 i->x-=offset.x();
301 i->y-=offset.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
307 else
309 if (cnt)
311 newLine(rflags);
312 flags|=GS_ISFIRST;
317 int xadvance=glyph->xadvance, kern=0;
319 if (previous && use_kerning)
321 FT_Vector delta;
322 FT_Get_Kerning(current_face, previous, glyphIndex, ft_kerning_default, &delta);
323 kern=delta.x>>6;
326 pGlyph ng;
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 );
332 xadvance+=kern;
334 ng.x=cursor.x()+kern;
335 ng.y=cursor.y();
336 ng.w=xadvance;
337 ng.font=current_font;
338 ng.font->lock();
339 ng.glyph_index=glyphIndex;
340 ng.flags=flags;
342 glyphs.push_back(ng);
344 cursor+=ePoint(xadvance, 0);
345 previous=glyphIndex;
346 return 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 )
360 continue;
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() );
371 if ( glyphs.size() )
372 bboxValid=1;
375 void eTextPara::newLine(int flags)
377 if (maximum.width()<cursor.x())
378 maximum.setWidth(cursor.x());
379 cursor.setX(left);
380 previous=0;
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());
385 previous=0;
388 eTextPara::~eTextPara()
390 clear();
391 if (refcnt>=0)
392 eFatal("verdammt man der war noch gelockt :/\n");
395 void eTextPara::destroy()
397 if (!refcnt--)
398 delete this;
401 eTextPara *eTextPara::grab()
403 refcnt++;
404 return this;
407 void eTextPara::setFont(const gFont &font)
409 if (refcnt)
410 eFatal("mod. after lock");
412 Font *fnt=fontRenderClass::getInstance()->getFont(font.family.c_str(), font.pointSize);
413 if (!fnt)
414 eWarning("FONT '%s' MISSING!", font.family.c_str());
415 setFont(fnt,
416 fontRenderClass::getInstance()->getFont(replacement_facename.c_str(), font.pointSize));
419 eString eTextPara::replacement_facename;
421 void eTextPara::setFont(Font *fnt, Font *replacement)
423 if (refcnt)
424 eFatal("mod. after lock");
426 if (!fnt)
427 return;
429 if (current_font)
431 if ( !current_font->ref )
432 delete current_font;
434 current_font=fnt;
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!");
451 return;
454 if (current_font)
456 if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, &current_font->font.font, &current_face, &current_font->size)<0)
458 eDebug("FTC_Manager_Lookup_Size failed!");
459 return;
462 cache_current_font=&current_font->font.font;
463 previous=0;
464 use_kerning=FT_HAS_KERNING(current_face);
467 void
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);
474 if (refcnt)
475 eFatal("mod. after lock");
477 if (!current_font)
478 return -1;
480 if (cursor.y()==-1)
482 cursor=ePoint(area.x(), area.y()+(current_face->size->metrics.ascender>>6));
483 left=cursor.x();
486 if (&current_font->font.font != cache_current_font)
488 if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, &current_font->font.font, &current_face, &current_font->size)<0)
490 eDebug("FTC_Manager_Lookup_Size failed!");
491 return -1;
493 cache_current_font=&current_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
509 unicode&=0x1F;
510 unicode<<=6;
511 if (p != string.end())
512 unicode|=(*p++)&0x3F;
513 } else if ((unicode & 0xF0)==0xE0) // three bytes
515 unicode&=0x0F;
516 unicode<<=6;
517 if (p != string.end())
518 unicode|=(*p++)&0x3F;
519 unicode<<=6;
520 if (p != string.end())
521 unicode|=(*p++)&0x3F;
522 } else if ((unicode & 0xF8)==0xF0) // four bytes
524 unicode&=0x07;
525 unicode<<=6;
526 if (p != string.end())
527 unicode|=(*p++)&0x3F;
528 unicode<<=6;
529 if (p != string.end())
530 unicode|=(*p++)&0x3F;
531 unicode<<=6;
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
545 #ifdef HAVE_FRIBIDI
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);
556 #else
557 uc_visual=uc_shape;
558 #endif
560 glyphs.reserve(uc_visual.size());
562 int nextflags = 0;
564 for (std::vector<unsigned long>::const_iterator i(uc_visual.begin());
565 i != uc_visual.end(); ++i)
567 int isprintable=1;
568 int flags = nextflags;
569 nextflags = 0;
570 if (!(rflags&RS_DIRECT))
572 switch (*i)
574 case '\\':
576 unsigned long c = *(i+1);
577 switch (c)
579 case 'n':
580 i++;
581 goto newline;
582 case 't':
583 i++;
584 goto tab;
585 case 'r':
586 i++;
587 goto nprint;
588 default:
591 break;
593 case '\t':
594 tab: isprintable=0;
595 cursor+=ePoint(current_font->tabwidth, 0);
596 cursor-=ePoint(cursor.x()%current_font->tabwidth, 0);
597 break;
598 case 0x8A:
599 case 0xE08A:
600 case '\n':
601 newline:isprintable=0;
602 newLine(rflags);
603 nextflags|=GS_ISFIRST;
604 break;
605 case '\r':
606 case 0x86: case 0xE086:
607 case 0x87: case 0xE087:
608 nprint: isprintable=0;
609 break;
610 case ' ':
611 flags|=GS_ISSPACE;
612 default:
613 break;
616 if (isprintable)
618 FT_UInt index;
620 index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(current_face, *i);
622 if (!index)
624 if (replacement_face)
625 index=(rflags&RS_DIRECT)? *i : FT_Get_Char_Index(replacement_face, *i);
627 if (!index)
628 eDebug("unicode %d ('%c') not present", *i, *i);
629 else
630 appendGlyph(replacement_font, replacement_face, index, flags, rflags);
631 } else
632 appendGlyph(current_font, current_face, index, flags, rflags);
635 bboxValid=false;
636 calc_bbox();
637 #ifdef HAVE_FRIBIDI
638 if (dir & FRIBIDI_MASK_RTL)
639 realign(dirRight);
640 #endif
641 return 0;
644 void eTextPara::blit(gPixmapDC &dc, const ePoint &offset, const gRGB &background, const gRGB &foreground)
646 singleLock s(ftlock);
648 if (!current_font)
649 return;
651 if (&current_font->font.font != cache_current_font)
653 if (FTC_Manager_Lookup_Size(fontRenderClass::instance->cacheManager, &current_font->font.font, &current_face, &current_font->size)<0)
655 eDebug("FTC_Manager_Lookup_Size failed!");
656 return;
658 cache_current_font=&current_font->font.font;
661 gPixmap &target=dc.getPixmap();
663 register int opcode;
664 gColor *lookup8=0;
665 __u32 lookup32[16];
667 if (target.bpp == 8)
669 if (target.clut.data)
671 lookup8=getColor(target.clut, background, foreground).lookup;
672 opcode=0;
673 } else
674 opcode=1;
675 } else if (target.bpp == 32)
677 opcode=3;
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;
686 } else
688 for (int i=0; i<16; ++i)
689 lookup32[i]=(0x010101*i)|0xFF000000;
691 } else
693 eWarning("can't render to %dbpp", target.bpp);
694 return;
697 eRect clip(0, 0, target.x, target.y);
698 clip&=dc.getClip();
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))
706 continue;
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())
714 sy=clip.bottom()-ry;
715 if ((sx+rx) >= clip.right())
716 sx=clip.right()-rx;
717 if (rx < clip.left())
719 int diff=clip.left()-rx;
720 s+=diff;
721 sx-=diff;
722 rx+=diff;
723 d+=diff*target.bypp;
725 if (ry < clip.top())
727 int diff=clip.top()-ry;
728 s+=diff*glyph_bitmap->pitch;
729 sy-=diff;
730 ry+=diff;
731 d+=diff*buffer_stride;
733 if (sx>0)
734 for (int ay=0; ay<sy; ay++)
736 if (!opcode) // 4bit lookup to 8bit
738 register __u8 *td=d;
739 register int ax;
740 for (ax=0; ax<sx; ax++)
742 register int b=(*s++)>>4;
743 if(b)
744 *td++=lookup8[b];
745 else
746 td++;
748 } else if (opcode == 1) // 8bit direct
750 register __u8 *td=d;
751 register int ax;
752 for (ax=0; ax<sx; ax++)
754 register int b=*s++;
755 *td++^=b;
757 } else
759 register __u32 *td=(__u32*)d;
760 register int ax;
761 for (ax=0; ax<sx; ax++)
763 register int b=(*s++)>>4;
764 if(b)
765 *td++=lookup32[b];
766 else
767 td++;
770 s+=glyph_bitmap->pitch-sx;
771 d+=buffer_stride;
776 void eTextPara::realign(int dir) // der code hier ist ein wenig merkwuerdig.
778 if (refcnt)
779 eFatal("mod. after lock");
781 if (dir==dirLeft)
782 return;
784 glyphString::iterator begin(glyphs.begin()), c(glyphs.begin()), end(glyphs.begin()), last;
785 while (c != glyphs.end())
787 int linelength=0;
788 int numspaces=0, num=0;
789 begin=end;
791 ASSERT( end != glyphs.end());
793 // zeilenende suchen
794 do {
795 last=end;
796 ++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))
804 continue;
806 if (c->flags&GS_ISSPACE)
807 numspaces++;
808 linelength+=c->w;
809 num++;
812 if (!num) // line mit nur einem space
813 continue;
815 switch (dir)
817 case dirRight:
818 case dirCenter:
820 int offset=area.width()-linelength;
821 if (dir==dirCenter)
822 offset/=2;
823 offset+=area.left();
824 while (begin != end)
826 begin->bbox.moveBy(offset-begin->x,0);
827 begin->x=offset;
828 offset+=begin->w;
829 ++begin;
831 break;
833 case dirBlock:
835 if (end == glyphs.end()) // letzte zeile linksbuendig lassen
836 continue;
837 int spacemode;
838 if (numspaces)
839 spacemode=1;
840 else
841 spacemode=0;
842 if ((!spacemode) && (num<2))
843 break;
844 int off=(area.width()-linelength)*256/(spacemode?numspaces:(num-1));
845 int curoff=0;
846 while (begin != end)
848 int doadd=0;
849 if ((!spacemode) || (begin->flags&GS_ISSPACE))
850 doadd=1;
851 begin->x+=curoff>>8;
852 begin->bbox.moveBy(curoff>>8,0);
853 if (doadd)
854 curoff+=off;
855 ++begin;
857 break;
861 bboxValid=false;
862 calc_bbox();
865 void eTextPara::clear()
867 singleLock s(ftlock);
869 if ( current_font && !current_font->ref )
870 delete current_font;
872 if ( replacement_font && !replacement_font->ref )
873 delete replacement_font;
875 for (glyphString::iterator i(glyphs.begin()); i!=glyphs.end(); ++i)
876 i->font->unlock();
877 glyphs.clear();
880 eAutoInitP0<fontRenderClass> init_fontRenderClass(eAutoInitNumbers::graphic-1, "Font Render Class");