Merge branch 'ryzom/ark-features' into main/gingo-test
[ryzomcore.git] / nel / src / 3d / font_generator.cpp
blob96a5945f3445381732452bbfcc31635cf397a305
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010-2018 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2015-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
6 //
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/>.
20 #include "std3d.h"
22 #include <string>
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"
33 using namespace std;
35 #ifndef NL_DONT_USE_EXTERNAL_CODE
37 #include <ft2build.h>
38 #include FT_FREETYPE_H
39 #include FT_SYNTHESIS_H
41 // for freetype 2.0
42 #ifdef FTERRORS_H
43 #undef FTERRORS_H
44 #endif
46 // for freetype 2.0.1
47 #ifdef __FTERRORS_H__
48 #undef __FTERRORS_H__
49 #endif
51 #define FT_ERRORDEF( e, v, s ) { e, s },
52 #define FT_ERROR_START_LIST {
53 #define FT_ERROR_END_LIST { 0, 0 } };
55 const struct
57 int err_code;
58 const char* err_msg;
59 } ft_errors[] =
61 #include FT_ERRORS_H
63 using namespace NLMISC;
65 #ifdef DEBUG_NEW
66 #define new DEBUG_NEW
67 #endif
69 namespace NL3D {
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);
85 return ukn;
88 std::string CFontGenerator::getFontFileName() const
90 return _FontFileName;
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
102 if (count > 0)
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());
118 count = 0;
121 else
123 nlwarning("Unable to seek to position %u of %s", (uint)offset, file->getStreamName().c_str());
124 count = 0;
128 return count;
131 // Freetype will call this function when it won't need to access to file anymore
132 static void nlFreetypeStreamClose(FT_Stream stream)
134 if (!stream) return;
136 // get a pointer on our CIFile
137 CIFile *file = (CIFile*)stream->descriptor.pointer;
139 if (file)
141 // close and delete file
142 file->close();
143 delete file;
145 stream->descriptor.pointer = NULL;
148 // free Freetype stream structure
149 free(stream);
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());
160 return false;
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());
170 delete file;
171 return false;
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;
182 return true;
186 * Constructor
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);
203 else
205 _FontFileNames.push_back(fontFileName);
208 FT_Error error;
210 if (!_LibraryInit)
212 error = FT_Init_FreeType (&_Library);
213 if (error)
215 nlerror ("FT_Init_FreeType() failed: %s", getFT2Error(error));
218 ++_LibraryInit;
220 for (std::vector<std::string>::iterator it(_FontFileNames.begin()), end(_FontFileNames.end()); it != end; ++it)
222 FT_Open_Args args;
223 const std::string &fileName = *it;
225 if (!createFreetypeStream(fileName, args))
227 nlerror("createFreetypeStream failed with file '%s'", fileName.c_str());
230 FT_Face face;
231 error = FT_Open_Face(_Library, &args, 0, &face);
232 if (error)
234 nlerror("FT_New_Face() failed with file '%s': %s", fileName.c_str(), getFT2Error(error));
237 string fontEx = fontExFileName;
238 if (fontEx.empty())
240 // try to see if the ex filename exists based on the fontExFileName
241 fontEx = CPath::lookup(CFile::getFilenameWithoutExtension(fileName) + ".afm", false, false);
244 if (!fontEx.empty())
246 if (!createFreetypeStream(fontEx, args))
248 nlerror("createFreetypeStream failed with file '%s'", fileName.c_str());
249 FT_Done_Face(face);
250 continue;
253 error = FT_Attach_Stream(face, &args);
254 if (error)
256 nlwarning("FT_Attach_File() failed with file '%s': %s", fontEx.c_str(), getFT2Error(error));
257 FT_Done_Face(face);
258 continue;
262 error = FT_Select_Charmap(face, ft_encoding_unicode);
263 if (error)
265 nlerror("FT_Select_Charmap() failed with file '%s': %s", fileName.c_str(), getFT2Error(error));
266 FT_Done_Face(face);
267 continue;
270 _Faces.push_back(face);
274 CFontGenerator::~CFontGenerator ()
276 for (std::vector<FT_Face>::iterator it(_Faces.begin()), end(_Faces.end()); it != end; ++it)
277 FT_Done_Face(*it);
278 _Faces.clear();
280 // important: destroying and creating your last font generator
281 // will also reload the freetype library
282 nlassert(_LibraryInit);
283 --_LibraryInit;
284 if (!_LibraryInit)
286 FT_Done_FreeType(_Library);
287 _Library = NULL;
291 void CFontGenerator::getSizes (u32char c, uint32 size, uint32 &width, uint32 &height)
293 FT_Error error;
294 FT_Face face;
295 FT_UInt glyph_index = 0;
297 if (!_Faces.size())
299 nlerror("No faces loaded");
300 return;
303 for (std::vector<FT_Face>::iterator it(_Faces.begin()), end(_Faces.end()); it != end; ++it)
305 face = *it;
306 error = FT_Set_Pixel_Sizes(face, size, size);
307 if (error)
309 nlerror("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error));
310 continue;
313 // retrieve glyph index from character code
314 glyph_index = FT_Get_Char_Index(face, c);
316 // found glyph
317 if (glyph_index)
318 break;
321 // load glyph image into the slot (erase previous one)
322 error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT);
323 if (error)
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);
329 if (error)
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)
342 FT_Error error;
343 FT_Face face;
344 FT_UInt glyph_index = 0;
346 if (!_Faces.size())
348 nlerror("No faces loaded");
349 return NULL;
352 for (std::vector<FT_Face>::iterator it(_Faces.begin()), end(_Faces.end()); it != end; ++it)
354 face = *it;
355 error = FT_Set_Pixel_Sizes(face, size, size);
356 if (error)
358 nlerror("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error));
359 continue;
362 // retrieve glyph index from character code
363 glyph_index = FT_Get_Char_Index(face, c);
365 // found glyph
366 if (glyph_index)
367 break;
370 // load glyph image into the slot (erase previous one)
371 error = FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT);
372 if (error)
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);
378 if (error)
380 nlerror("FT_Load_Glyph() fallback failed: %s", getFT2Error(error));
384 if (size == 0)
386 width = 0;
387 height = 0;
388 pitch = 0;
389 left = 0;
390 top = 0;
391 advx = 0;
392 glyphIndex = glyph_index;
393 return NULL;
396 if (embolden)
398 FT_GlyphSlot_Embolden(face->glyph);
401 if (oblique)
403 FT_GlyphSlot_Oblique(face->glyph);
406 // convert to an anti-aliased bitmap
407 error = FT_Render_Glyph (face->glyph, ft_render_mode_normal);
408 if (error)
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]))
433 kernx = 0;
435 else
437 // This is currently not used...
439 FT_Vector kerning;
440 FT_Error error = FT_Get_Kerning (_Faces[0], left, right, ft_kerning_default, &kerning);
441 if (error)
443 nlerror ("FT_Get_Kerning() failed: %s", getFT2Error(error));
445 kernx = kerning.x;
451 uint32 CFontGenerator::getCharIndex (u32char c)
453 // This is currently not used...
455 uint32 ret = FT_Get_Char_Index(_Faces[0], c);
457 if (ret == 0)
459 // no glyph available, replace with a dot
460 ret = FT_Get_Char_Index (_Faces[0], u32char('.'));
463 return ret;
466 } // NL3D
468 #else // NL_DONT_USE_EXTERNAL_CODE
470 #define NOMINMAX
471 #include <windows.h>
473 using namespace NLMISC;
475 namespace NL3D {
477 HBITMAP Dib;
478 uint32 *Buffer;
479 HDC hdcDib;
480 int Width = 100;
481 int Height = 100;
484 * Constructor
486 CFontGenerator::CFontGenerator (const std::string &fontFileName, const std::string &fontExFileName)
489 // HWND win=winHack;
490 // WindowHandle = win;
491 // Format = format;
492 // RECT rect;
493 // BOOL ret = GetClientRect( WindowHandle, &rect);
494 // assert (ret);
496 // uint f = AddFontResource ("");
497 // nlassert (f);
499 BITMAPINFO info;
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);
513 nlassert (hdc);
514 Dib = CreateDIBSection (hdc, &info, DIB_RGB_COLORS, (void**)&Buffer, NULL, NULL);
516 hdcDib = CreateCompatibleDC (hdc);
517 nlassert (hdcDib);
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 ()
529 DeleteObject (Dib);
531 DeleteDC (hdcDib);
534 void CFontGenerator::getSizes (u32char c, uint32 size, uint32 &width, uint32 &height)
538 HFONT hFont = NULL;
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)
543 if (size == 0)
545 width = 0;
546 height = 0;
547 pitch = 0;
548 left = 0;
549 top = 0;
550 advx = 0;
551 glyphIndex = 0;
552 return NULL;
555 // Create the font
556 if (hFont == NULL || CurrentFontSize != size)
558 if (hFont != NULL)
560 DeleteObject (hFont);
563 hFont=CreateFont
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
580 nlassert (hFont);
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
591 DWORD glyphIndex;
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
597 cc = '.';
601 RECT rect;
602 rect.bottom = Height;
603 rect.top = 0;
604 rect.left = 0;
605 rect.right = Width;
607 int res = DrawTextW (hdcDib, &cc, 1, &rect, DT_LEFT | DT_TOP);
609 POINT point;
610 point.y = res;
612 int w = res;
613 // BOOL rey = GetCharWidth32 (hdcDib, (uint8) cc, (uint8) cc, &w);
614 BOOL rey = GetCharWidth32 (hdcDib, cc, cc, &w);
615 nlassert (rey);
616 point.x = w;
618 // ABC abc;
619 // BOOL rey = GetCharABCWidths (hdcDib, (uint8) cc, (uint8) cc, &abc);
620 // nlassert (rey);
621 // point.x = abc.abcA;
623 SIZE s;
624 GetTextExtentPoint32W (hdcDib, &cc, 1, &s);
626 BOOL ret = LPtoDP (hdcDib, &point, 1);
627 nlassert (ret);
629 static uint8 buf[100*100];
631 sint32 _top = 0, _left = point.x;
632 sint32 right = 0, bottom = point.y;
633 bool emptyLine;
634 for (sint y = 0; y < point.y; y++)
636 emptyLine = true;
637 for (sint x = 0; x < point.x; x++)
639 buf[y*100+x] = (uint8) Buffer[y*100+x];
640 if (buf[y*100+x])
642 emptyLine = false;
643 if (x < _left)
644 _left = x;
645 if (x > right)
646 right = x;
649 // printf (buf[y*100+x]?"*":".");
651 if (!emptyLine)
653 if (_top == 0)
654 _top = y;
656 bottom = y;
659 // printf ("\n");
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;
665 pitch = 100;
666 advx = point.x;
668 WORD ag = 0;
669 glyphIndex = ag;
671 top = -_top;
672 left = -_left;
674 /* {
675 for (sint y = _top; y < _top + height; y++)
677 for (sint x = _left; x < _left + width; x++)
679 printf (buf[y*100+x]?"*":".");
681 printf ("\n");
683 printf ("w: %d h: %d s: %d a: %d l: %d t: %d", width, height, size, advx, left, top);
684 getchar();
687 _top = _left = top = left = 0;
688 top = s.cy;
689 width = s.cx;
690 height = s.cy;
692 return buf + _top * 100 + _left;
698 void CFontGenerator::getKerning (u32char left, u32char right, sint32 &kernx)
704 uint32 CFontGenerator::getCharIndex (u32char c)
706 return 0;
710 } // NL3D
712 #endif // NL_DONT_USE_EXTERNAL_CODE