1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2015-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "nel/misc/types_nl.h"
25 #include "nel/misc/debug.h"
26 #include "nel/misc/common.h"
27 #include "nel/misc/path.h"
28 #include "nel/misc/file.h"
29 #include "nel/misc/i18n.h"
31 #include "nel/3d/font_generator.h"
35 #ifndef NL_DONT_USE_EXTERNAL_CODE
38 #include FT_FREETYPE_H
39 #include FT_SYNTHESIS_H
51 #define FT_ERRORDEF( e, v, s ) { e, s },
52 #define FT_ERROR_START_LIST {
53 #define FT_ERROR_END_LIST { 0, 0 } };
63 using namespace NLMISC
;
71 FT_Library
CFontGenerator::_Library
= NULL
;
72 uint
CFontGenerator::_LibraryInit
= 0;
73 uint32
CFontGenerator::_FontGeneratorCounterUID
= 1;
75 const char *CFontGenerator::getFT2Error(FT_Error fte
)
77 static char ukn
[1024];
79 for (uint32 i
= 0; ft_errors
[i
].err_code
!= 0 || ft_errors
[i
].err_msg
!= 0; i
++)
81 if (ft_errors
[i
].err_code
== fte
)
82 return ft_errors
[i
].err_msg
;
84 smprintf (ukn
, 1024, "Unknown freetype2 error, errcode: 0x%x", fte
);
88 std::string
CFontGenerator::getFontFileName() const
93 CFontGenerator
*newCFontGenerator(const std::string
&fontFileName
)
95 return new CFontGenerator(fontFileName
);
98 // Freetype will call this function to get a buffer in data
99 static unsigned long nlFreetypeStreamIo(FT_Stream stream
, unsigned long offset
, unsigned char* buffer
, unsigned long count
)
101 // if count is 0, we don't need to do anything
104 // get a pointer on our CIFile
105 CIFile
*file
= (CIFile
*)stream
->descriptor
.pointer
;
107 // try to seek to offset
108 if (file
->seek(offset
, IStream::begin
))
112 // try to fill buffer with data from file
113 file
->serialBuffer(buffer
, count
);
115 catch(const EFile
&e
)
117 nlwarning("Unable to read %u bytes from position %u of %s", (uint
)count
, (uint
)offset
, file
->getStreamName().c_str());
123 nlwarning("Unable to seek to position %u of %s", (uint
)offset
, file
->getStreamName().c_str());
131 // Freetype will call this function when it won't need to access to file anymore
132 static void nlFreetypeStreamClose(FT_Stream stream
)
136 // get a pointer on our CIFile
137 CIFile
*file
= (CIFile
*)stream
->descriptor
.pointer
;
141 // close and delete file
145 stream
->descriptor
.pointer
= NULL
;
148 // free Freetype stream structure
152 // helper to open a font and use our functions to handle BNP files and UTF-8 filenames
153 static bool createFreetypeStream(const std::string
&filename
, FT_Open_Args
&args
)
155 CIFile
*file
= new CIFile();
157 if (!file
->open(filename
))
159 nlwarning("Unable to open %s", filename
.c_str());
163 args
.flags
= FT_OPEN_STREAM
;
164 args
.stream
= (FT_Stream
)malloc(sizeof(*args
.stream
));
166 if (args
.stream
== NULL
)
168 nlwarning("Unable to allocate FT_Stream for %s", filename
.c_str());
174 args
.stream
->base
= NULL
; // only used for memory streams
175 args
.stream
->size
= file
->getFileSize();
176 args
.stream
->pos
= 0;
177 args
.stream
->descriptor
.pointer
= file
;
178 args
.stream
->pathname
.pointer
= NULL
; // filename is already managed by CIFile
179 args
.stream
->read
= nlFreetypeStreamIo
;
180 args
.stream
->close
= nlFreetypeStreamClose
;
188 CFontGenerator::CFontGenerator (const std::string
&fontFileName
, const std::string
&fontExFileName
)
190 _UID
= _FontGeneratorCounterUID
;
191 _FontGeneratorCounterUID
++;
192 _FontFileName
= fontFileName
;
194 if (NLMISC::startsWith(fontFileName
, "ui"))
196 std::string nameList
= CI18N::get(fontFileName
);
197 NLMISC::explode(nameList
, string("\n"), _FontFileNames
, true);
198 for (std::vector
<std::string
>::iterator
it(_FontFileNames
.begin()), end(_FontFileNames
.end()); it
!= end
; ++it
)
200 *it
= CPath::lookup(*it
);
205 _FontFileNames
.push_back(fontFileName
);
212 error
= FT_Init_FreeType (&_Library
);
215 nlerror ("FT_Init_FreeType() failed: %s", getFT2Error(error
));
220 for (std::vector
<std::string
>::iterator
it(_FontFileNames
.begin()), end(_FontFileNames
.end()); it
!= end
; ++it
)
223 const std::string
&fileName
= *it
;
225 if (!createFreetypeStream(fileName
, args
))
227 nlerror("createFreetypeStream failed with file '%s'", fileName
.c_str());
231 error
= FT_Open_Face(_Library
, &args
, 0, &face
);
234 nlerror("FT_New_Face() failed with file '%s': %s", fileName
.c_str(), getFT2Error(error
));
237 string fontEx
= fontExFileName
;
240 // try to see if the ex filename exists based on the fontExFileName
241 fontEx
= CPath::lookup(CFile::getFilenameWithoutExtension(fileName
) + ".afm", false, false);
246 if (!createFreetypeStream(fontEx
, args
))
248 nlerror("createFreetypeStream failed with file '%s'", fileName
.c_str());
253 error
= FT_Attach_Stream(face
, &args
);
256 nlwarning("FT_Attach_File() failed with file '%s': %s", fontEx
.c_str(), getFT2Error(error
));
262 error
= FT_Select_Charmap(face
, ft_encoding_unicode
);
265 nlerror("FT_Select_Charmap() failed with file '%s': %s", fileName
.c_str(), getFT2Error(error
));
270 _Faces
.push_back(face
);
274 CFontGenerator::~CFontGenerator ()
276 for (std::vector
<FT_Face
>::iterator
it(_Faces
.begin()), end(_Faces
.end()); it
!= end
; ++it
)
280 // important: destroying and creating your last font generator
281 // will also reload the freetype library
282 nlassert(_LibraryInit
);
286 FT_Done_FreeType(_Library
);
291 void CFontGenerator::getSizes (u32char c
, uint32 size
, uint32
&width
, uint32
&height
)
295 FT_UInt glyph_index
= 0;
299 nlerror("No faces loaded");
303 for (std::vector
<FT_Face
>::iterator
it(_Faces
.begin()), end(_Faces
.end()); it
!= end
; ++it
)
306 error
= FT_Set_Pixel_Sizes(face
, size
, size
);
309 nlerror("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error
));
313 // retrieve glyph index from character code
314 glyph_index
= FT_Get_Char_Index(face
, c
);
321 // load glyph image into the slot (erase previous one)
322 error
= FT_Load_Glyph(face
, glyph_index
, FT_LOAD_DEFAULT
);
325 // use fallback for glyph/character errors (composite char limit for example)
326 nlwarning ("FT_Load_Glyph() failed: %s", getFT2Error(error
));
328 error
= FT_Load_Glyph(face
, 0, FT_LOAD_DEFAULT
);
331 nlerror("FT_Load_Glyph() fallback failed: %s", getFT2Error(error
));
335 // convert 24.6 fixed point into integer
336 width
= face
->glyph
->metrics
.width
>> 6;
337 height
= face
->glyph
->metrics
.height
>> 6;
340 uint8
*CFontGenerator::getBitmap (u32char c
, uint32 size
, bool embolden
, bool oblique
, uint32
&width
, uint32
&height
, uint32
&pitch
, sint32
&left
, sint32
&top
, sint32
&advx
, uint32
&glyphIndex
)
344 FT_UInt glyph_index
= 0;
348 nlerror("No faces loaded");
352 for (std::vector
<FT_Face
>::iterator
it(_Faces
.begin()), end(_Faces
.end()); it
!= end
; ++it
)
355 error
= FT_Set_Pixel_Sizes(face
, size
, size
);
358 nlerror("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error
));
362 // retrieve glyph index from character code
363 glyph_index
= FT_Get_Char_Index(face
, c
);
370 // load glyph image into the slot (erase previous one)
371 error
= FT_Load_Glyph (face
, glyph_index
, FT_LOAD_DEFAULT
);
374 // use fallback for glyph/character errors (composite char limit for example)
375 nlwarning ("FT_Load_Glyph() failed: %s", getFT2Error(error
));
377 error
= FT_Load_Glyph (face
, 0, FT_LOAD_DEFAULT
);
380 nlerror("FT_Load_Glyph() fallback failed: %s", getFT2Error(error
));
392 glyphIndex
= glyph_index
;
398 FT_GlyphSlot_Embolden(face
->glyph
);
403 FT_GlyphSlot_Oblique(face
->glyph
);
406 // convert to an anti-aliased bitmap
407 error
= FT_Render_Glyph (face
->glyph
, ft_render_mode_normal
);
410 nlerror ("FT_Render_Glyph() failed: %s", getFT2Error(error
));
413 width
= face
->glyph
->bitmap
.width
;
414 height
= face
->glyph
->bitmap
.rows
;
415 pitch
= face
->glyph
->bitmap
.pitch
;
417 left
= face
->glyph
->bitmap_left
;
418 top
= face
->glyph
->bitmap_top
;
420 advx
= face
->glyph
->advance
.x
>> 6;
422 glyphIndex
= glyph_index
;
424 return (uint8
*) face
->glyph
->bitmap
.buffer
;
429 void CFontGenerator::getKerning (u32char left
, u32char right
, sint32
&kernx
)
431 if (!FT_HAS_KERNING(_Faces
[0]))
437 // This is currently not used...
440 FT_Error error
= FT_Get_Kerning (_Faces
[0], left
, right
, ft_kerning_default
, &kerning
);
443 nlerror ("FT_Get_Kerning() failed: %s", getFT2Error(error
));
451 uint32
CFontGenerator::getCharIndex (u32char c
)
453 // This is currently not used...
455 uint32 ret
= FT_Get_Char_Index(_Faces
[0], c
);
459 // no glyph available, replace with a dot
460 ret
= FT_Get_Char_Index (_Faces
[0], u32char('.'));
468 #else // NL_DONT_USE_EXTERNAL_CODE
473 using namespace NLMISC
;
486 CFontGenerator::CFontGenerator (const std::string
&fontFileName
, const std::string
&fontExFileName
)
490 // WindowHandle = win;
493 // BOOL ret = GetClientRect( WindowHandle, &rect);
496 // uint f = AddFontResource ("");
500 info
.bmiHeader
.biWidth
= Width
;
501 info
.bmiHeader
.biHeight
= -Height
;
502 info
.bmiHeader
.biSize
= sizeof (BITMAPINFOHEADER
);
503 info
.bmiHeader
.biPlanes
= 1;
504 info
.bmiHeader
.biBitCount
= 32;
505 info
.bmiHeader
.biCompression
= BI_RGB
;
506 info
.bmiHeader
.biSizeImage
= 4*Width
*Height
;
507 info
.bmiHeader
.biXPelsPerMeter
= 1;
508 info
.bmiHeader
.biYPelsPerMeter
= 1;
509 info
.bmiHeader
.biClrUsed
= 0;
510 info
.bmiHeader
.biClrImportant
= 0;
512 HDC hdc
= GetDC (NULL
);
514 Dib
= CreateDIBSection (hdc
, &info
, DIB_RGB_COLORS
, (void**)&Buffer
, NULL
, NULL
);
516 hdcDib
= CreateCompatibleDC (hdc
);
520 ReleaseDC (NULL
, hdc
);
522 SetTextAlign (hdcDib
, TA_TOP
| TA_LEFT
| TA_NOUPDATECP
);
523 SetBkColor (hdcDib
, RGB (0,0,0));
524 SetTextColor (hdcDib
, RGB (255, 255, 255));
527 CFontGenerator::~CFontGenerator ()
534 void CFontGenerator::getSizes (u32char c
, uint32 size
, uint32
&width
, uint32
&height
)
539 uint32 CurrentFontSize
= 0;
541 uint8
*CFontGenerator::getBitmap (u32char c
, uint32 size
, bool embolden
, bool oblique
, uint32
&width
, uint32
&height
, uint32
&pitch
, sint32
&left
, sint32
&top
, sint32
&advx
, uint32
&glyphIndex
)
556 if (hFont
== NULL
|| CurrentFontSize
!= size
)
560 DeleteObject (hFont
);
565 size
, // logical height of font
566 0, // logical average character width
567 0, // angle of escapement
568 0, // base-line orientation angle
569 FW_DONTCARE
, //FW_NORMAL, // font weight
570 FALSE
, // italic attribute flag
571 FALSE
, // underline attribute flag
572 FALSE
, // strikeout attribute flag
573 DEFAULT_CHARSET
, // character set identifier
574 OUT_DEVICE_PRECIS
, // output precision
575 CLIP_DEFAULT_PRECIS
, // clipping precision
576 DEFAULT_QUALITY
, // output quality
577 DEFAULT_PITCH
|FF_DONTCARE
, // pitch and family
578 "Arial Unicode MS Normal" // pointer to typeface name string
582 CurrentFontSize
= size
;
585 SelectObject (hdcDib
, hFont
);
586 SelectObject (hdcDib
, Dib
);
588 const u32char cc
= /*(char)*/ c
;
590 // prevent outputing white glyph if char is not available in font
592 if (GetGlyphIndicesW(hdcDib
, &cc
, 1, &glyphIndex
, GGI_MARK_NONEXISTING_GLYPHS
) == 1);
594 if (glyphIndex
== 0xffff)
596 // thee char is unsupported, replace with a dot
602 rect
.bottom
= Height
;
607 int res
= DrawTextW (hdcDib
, &cc
, 1, &rect
, DT_LEFT
| DT_TOP
);
613 // BOOL rey = GetCharWidth32 (hdcDib, (uint8) cc, (uint8) cc, &w);
614 BOOL rey
= GetCharWidth32 (hdcDib
, cc
, cc
, &w
);
619 // BOOL rey = GetCharABCWidths (hdcDib, (uint8) cc, (uint8) cc, &abc);
621 // point.x = abc.abcA;
624 GetTextExtentPoint32W (hdcDib
, &cc
, 1, &s
);
626 BOOL ret
= LPtoDP (hdcDib
, &point
, 1);
629 static uint8 buf
[100*100];
631 sint32 _top
= 0, _left
= point
.x
;
632 sint32 right
= 0, bottom
= point
.y
;
634 for (sint y
= 0; y
< point
.y
; y
++)
637 for (sint x
= 0; x
< point
.x
; x
++)
639 buf
[y
*100+x
] = (uint8
) Buffer
[y
*100+x
];
649 // printf (buf[y*100+x]?"*":".");
661 width
= right
- _left
+ 1;
662 if (right
- _left
+ 1 < 0) width
= 0;
663 height
= bottom
- _top
+ 1;
664 if (bottom
- _top
+ 1 < 0) height
= 0;
675 for (sint y = _top; y < _top + height; y++)
677 for (sint x = _left; x < _left + width; x++)
679 printf (buf[y*100+x]?"*":".");
683 printf ("w: %d h: %d s: %d a: %d l: %d t: %d", width, height, size, advx, left, top);
687 _top
= _left
= top
= left
= 0;
692 return buf
+ _top
* 100 + _left
;
698 void CFontGenerator::getKerning (u32char left
, u32char right
, sint32
&kernx
)
704 uint32
CFontGenerator::getCharIndex (u32char c
)
712 #endif // NL_DONT_USE_EXTERNAL_CODE