// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #include "std3d.h" #include #include "nel/misc/types_nl.h" #include "nel/misc/debug.h" #include "nel/misc/common.h" #include "nel/misc/path.h" #include "nel/misc/file.h" #include "nel/3d/font_generator.h" using namespace std; #ifndef NL_DONT_USE_EXTERNAL_CODE #include #include FT_FREETYPE_H #include FT_SYNTHESIS_H // for freetype 2.0 #ifdef FTERRORS_H #undef FTERRORS_H #endif // for freetype 2.0.1 #ifdef __FTERRORS_H__ #undef __FTERRORS_H__ #endif #define FT_ERRORDEF( e, v, s ) { e, s }, #define FT_ERROR_START_LIST { #define FT_ERROR_END_LIST { 0, 0 } }; const struct { int err_code; const char* err_msg; } ft_errors[] = #include FT_ERRORS_H using namespace NLMISC; namespace NL3D { FT_Library CFontGenerator::_Library = NULL; uint CFontGenerator::_LibraryInit = 0; uint32 CFontGenerator::_FontGeneratorCounterUID = 1; const char *CFontGenerator::getFT2Error(FT_Error fte) { static char ukn[1024]; for (uint32 i = 0; ft_errors[i].err_code != 0 || ft_errors[i].err_msg != 0; i++) { if (ft_errors[i].err_code == fte) return ft_errors[i].err_msg; } smprintf (ukn, 1024, "Unknown freetype2 error, errcode: 0x%x", fte); return ukn; } CFontGenerator *newCFontGenerator(const std::string &fontFileName) { return new CFontGenerator(fontFileName); } // Freetype will call this function to get a buffer in data static unsigned long nlFreetypeStreamIo(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count) { // if count is 0, we don't need to do anything if (count > 0) { // get a pointer on our CIFile CIFile *file = (CIFile*)stream->descriptor.pointer; // try to seek to offset if (file->seek(offset, IStream::begin)) { try { // try to fill buffer with data from file file->serialBuffer(buffer, count); } catch(const EFile &e) { nlwarning("Unable to read %u bytes from position %u of %s", (uint)count, (uint)offset, file->getStreamName().c_str()); count = 0; } } else { nlwarning("Unable to seek to position %u of %s", (uint)offset, file->getStreamName().c_str()); count = 0; } } return count; } // Freetype will call this function when it won't need to access to file anymore static void nlFreetypeStreamClose(FT_Stream stream) { if (!stream) return; // get a pointer on our CIFile CIFile *file = (CIFile*)stream->descriptor.pointer; if (file) { // close and delete file file->close(); delete file; stream->descriptor.pointer = NULL; } // free Freetype stream structure free(stream); } // helper to open a font and use our functions to handle BNP files and UTF-8 filenames static bool createFreetypeStream(const std::string &filename, FT_Open_Args &args) { CIFile *file = new CIFile(); if (!file->open(filename)) { nlwarning("Unable to open %s", filename.c_str()); return false; } args.flags = FT_OPEN_STREAM; args.stream = (FT_Stream)malloc(sizeof(*args.stream)); if (args.stream == NULL) { nlwarning("Unable to allocate FT_Stream for %s", filename.c_str()); delete file; return false; } args.stream->base = NULL; // only used for memory streams args.stream->size = file->getFileSize(); args.stream->pos = 0; args.stream->descriptor.pointer = file; args.stream->pathname.pointer = NULL; // filename is already managed by CIFile args.stream->read = nlFreetypeStreamIo; args.stream->close = nlFreetypeStreamClose; return true; } /* * Constructor */ CFontGenerator::CFontGenerator (const std::string &fontFileName, const std::string &fontExFileName) { _UID = _FontGeneratorCounterUID; _FontGeneratorCounterUID++; _FontFileName = fontFileName; FT_Error error; if (!_LibraryInit) { error = FT_Init_FreeType (&_Library); if (error) { nlerror ("FT_Init_FreeType() failed: %s", getFT2Error(error)); } } ++_LibraryInit; FT_Open_Args args; if (!createFreetypeStream(fontFileName, args)) { nlerror ("createFreetypeStream failed with file '%s'", fontFileName.c_str()); } error = FT_Open_Face(_Library, &args, 0, &_Face); if (error) { nlerror ("FT_New_Face() failed with file '%s': %s", fontFileName.c_str(), getFT2Error(error)); } string fontEx = fontExFileName; if (fontEx.empty()) { // try to see if the ex filename exists based on the fontExFileName fontEx = CPath::lookup(CFile::getFilenameWithoutExtension (fontFileName)+".afm", false, false); } if (!fontEx.empty()) { if (!createFreetypeStream(fontEx, args)) { nlerror ("createFreetypeStream failed with file '%s'", fontFileName.c_str()); } error = FT_Attach_Stream(_Face, &args); if (error) { nlwarning ("FT_Attach_File() failed with file '%s': %s", fontEx.c_str(), getFT2Error(error)); } } error = FT_Select_Charmap (_Face, ft_encoding_unicode); if (error) { nlerror ("FT_Select_Charmap() failed with file '%s': %s", fontFileName.c_str(), getFT2Error(error)); } } CFontGenerator::~CFontGenerator () { // important: destroying and creating your last font generator // will also reload the freetype library nlassert(_LibraryInit); --_LibraryInit; if (!_LibraryInit) { FT_Done_FreeType(_Library); _Library = NULL; } } void CFontGenerator::getSizes (ucchar c, uint32 size, uint32 &width, uint32 &height) { FT_Error error; error = FT_Set_Pixel_Sizes (_Face, size, size); if (error) { nlerror ("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error)); } // retrieve glyph index from character code FT_UInt glyph_index = FT_Get_Char_Index (_Face, c); if (glyph_index == 0) { // no glyph available, replace with a dot glyph_index = FT_Get_Char_Index (_Face, ucchar('.')); } // load glyph image into the slot (erase previous one) error = FT_Load_Glyph (_Face, glyph_index, FT_LOAD_DEFAULT); if (error) { nlerror ("FT_Load_Glyph() failed: %s", getFT2Error(error)); } // convert 24.6 fixed point into integer width = _Face->glyph->metrics.width >> 6; height = _Face->glyph->metrics.height >> 6; } uint8 *CFontGenerator::getBitmap (ucchar c, uint32 size, bool embolden, bool oblique, uint32 &width, uint32 &height, uint32 &pitch, sint32 &left, sint32 &top, sint32 &advx, uint32 &glyphIndex) { FT_Error error; error = FT_Set_Pixel_Sizes (_Face, size, size); if (error) { nlerror ("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error)); } // retrieve glyph index from character code FT_UInt glyph_index = FT_Get_Char_Index (_Face, c); if (glyph_index == 0) { // no glyph available, replace with a dot glyph_index = FT_Get_Char_Index (_Face, ucchar('.')); } // load glyph image into the slot (erase previous one) error = FT_Load_Glyph (_Face, glyph_index, FT_LOAD_DEFAULT); if (error) { nlerror ("FT_Load_Glyph() failed: %s", getFT2Error(error)); } if (size == 0) { width = 0; height = 0; pitch = 0; left = 0; top = 0; advx = 0; glyphIndex = glyph_index; return NULL; } if (embolden) { FT_GlyphSlot_Embolden(_Face->glyph); } if (oblique) { FT_GlyphSlot_Oblique(_Face->glyph); } // convert to an anti-aliased bitmap error = FT_Render_Glyph (_Face->glyph, ft_render_mode_normal); if (error) { nlerror ("FT_Render_Glyph() failed: %s", getFT2Error(error)); } width = _Face->glyph->bitmap.width; height = _Face->glyph->bitmap.rows; pitch = _Face->glyph->bitmap.pitch; left = _Face->glyph->bitmap_left; top = _Face->glyph->bitmap_top; advx = _Face->glyph->advance.x >> 6; glyphIndex = glyph_index; return (uint8 *) _Face->glyph->bitmap.buffer; } void CFontGenerator::getKerning (ucchar left, ucchar right, sint32 &kernx) { if (!FT_HAS_KERNING(_Face)) { kernx = 0; } else { FT_Vector kerning; FT_Error error = FT_Get_Kerning (_Face, left, right, ft_kerning_default, &kerning); if (error) { nlerror ("FT_Get_Kerning() failed: %s", getFT2Error(error)); } kernx = kerning.x; } } uint32 CFontGenerator::getCharIndex (ucchar c) { uint32 ret = FT_Get_Char_Index(_Face, c); if (ret == 0) { // no glyph available, replace with a dot ret = FT_Get_Char_Index (_Face, ucchar('.')); } return ret; } } // NL3D #else // NL_DONT_USE_EXTERNAL_CODE #define NOMINMAX #include using namespace NLMISC; namespace NL3D { HBITMAP Dib; uint32 *Buffer; HDC hdcDib; int Width = 100; int Height = 100; /* * Constructor */ CFontGenerator::CFontGenerator (const std::string &fontFileName, const std::string &fontExFileName) { // HWND win=winHack; // WindowHandle = win; // Format = format; // RECT rect; // BOOL ret = GetClientRect( WindowHandle, &rect); // assert (ret); // uint f = AddFontResource (""); // nlassert (f); BITMAPINFO info; info.bmiHeader.biWidth = Width; info.bmiHeader.biHeight= -Height; info.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); info.bmiHeader.biPlanes = 1; info.bmiHeader.biBitCount = 32; info.bmiHeader.biCompression = BI_RGB; info.bmiHeader.biSizeImage = 4*Width*Height; info.bmiHeader.biXPelsPerMeter = 1; info.bmiHeader.biYPelsPerMeter = 1; info.bmiHeader.biClrUsed = 0; info.bmiHeader.biClrImportant = 0; HDC hdc = GetDC (NULL); nlassert (hdc); Dib = CreateDIBSection (hdc, &info, DIB_RGB_COLORS, (void**)&Buffer, NULL, NULL); hdcDib = CreateCompatibleDC (hdc); nlassert (hdcDib); ReleaseDC (NULL, hdc); SetTextAlign (hdcDib, TA_TOP | TA_LEFT | TA_NOUPDATECP); SetBkColor (hdcDib, RGB (0,0,0)); SetTextColor (hdcDib, RGB (255, 255, 255)); /*FT_Error error; if (!_LibraryInit) { error = FT_Init_FreeType (&_Library); if (error) { nlerror ("FT_Init_FreeType() failed: %s", getFT2Error(error)); } _LibraryInit = true; } error = FT_New_Face (_Library, fontFileName.c_str (), 0, &_Face); if (error) { nlerror ("FT_New_Face() failed with file '%s': %s", fontFileName.c_str(), getFT2Error(error)); } if (!fontExFileName.empty()) { error = FT_Attach_File (_Face, fontExFileName.c_str ()); if (error) { nlwarning ("FT_Attach_File() failed with file '%s': %s", fontExFileName.c_str(), getFT2Error(error)); } }*/ } CFontGenerator::~CFontGenerator () { DeleteObject (Dib); DeleteDC (hdcDib); } void CFontGenerator::getSizes (ucchar c, uint32 size, uint32 &width, uint32 &height) { /* FT_Error error; error = FT_Set_Pixel_Sizes (_Face, size, size); if (error) { nlerror ("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error)); } // retrieve glyph index from character code FT_UInt glyph_index = FT_Get_Char_Index (_Face, c); // load glyph image into the slot (erase previous one) error = FT_Load_Glyph (_Face, glyph_index, FT_LOAD_DEFAULT); if (error) { nlerror ("FT_Load_Glyph() failed: %s", getFT2Error(error)); } // convert 24.6 fixed point into integer width = _Face->glyph->metrics.width >> 6; height = _Face->glyph->metrics.height >> 6; */ } HFONT hFont = NULL; uint32 CurrentFontSize = 0; uint8 *CFontGenerator::getBitmap (ucchar c, uint32 size, bool embolden, bool oblique, uint32 &width, uint32 &height, uint32 &pitch, sint32 &left, sint32 &top, sint32 &advx, uint32 &glyphIndex) { /* FT_Error error; error = FT_Set_Pixel_Sizes (_Face, size, size); if (error) { nlerror ("FT_Set_Pixel_Sizes() failed: %s", getFT2Error(error)); } // retrieve glyph index from character code FT_UInt glyph_index = FT_Get_Char_Index (_Face, c); // load glyph image into the slot (erase previous one) error = FT_Load_Glyph (_Face, glyph_index, FT_LOAD_DEFAULT); if (error) { nlerror ("FT_Load_Glyph() failed: %s", getFT2Error(error)); } if (size == 0) { width = 0; height = 0; pitch = 0; left = 0; top = 0; advx = 0; glyphIndex = glyph_index; return NULL; } // convert to an anti-aliased bitmap error = FT_Render_Glyph (_Face->glyph, ft_render_mode_normal); if (error) { nlerror ("FT_Render_Glyph() failed: %s", getFT2Error(error)); } width = _Face->glyph->bitmap.width; height = _Face->glyph->bitmap.rows; pitch = _Face->glyph->bitmap.pitch; left = _Face->glyph->bitmap_left; top = _Face->glyph->bitmap_top; advx = _Face->glyph->advance.x >> 6; glyphIndex = glyph_index; return (uint8 *) _Face->glyph->bitmap.buffer; */ if (size == 0) { width = 0; height = 0; pitch = 0; left = 0; top = 0; advx = 0; glyphIndex = 0; return NULL; } // Create the font if (hFont == NULL || CurrentFontSize != size) { if (hFont != NULL) { DeleteObject (hFont); } hFont=CreateFont ( size, // logical height of font 0, // logical average character width 0, // angle of escapement 0, // base-line orientation angle FW_DONTCARE, //FW_NORMAL, // font weight FALSE, // italic attribute flag FALSE, // underline attribute flag FALSE, // strikeout attribute flag DEFAULT_CHARSET, // character set identifier OUT_DEVICE_PRECIS, // output precision CLIP_DEFAULT_PRECIS, // clipping precision DEFAULT_QUALITY, // output quality DEFAULT_PITCH|FF_DONTCARE, // pitch and family "Arial Unicode MS Normal" // pointer to typeface name string ); nlassert (hFont); CurrentFontSize = size; } SelectObject (hdcDib, hFont); SelectObject (hdcDib, Dib); const ucchar cc = /*(char)*/ c; // prevent outputing white glyph if char is not available in font DWORD glyphIndex; if (GetGlyphIndicesW(hdcDib, &cc, 1, &glyphIndex, GGI_MARK_NONEXISTING_GLYPHS) == 1); { if (glyphIndex == 0xffff) { // thee char is unsupported, replace with a dot cc = '.'; } } RECT rect; rect.bottom = Height; rect.top = 0; rect.left = 0; rect.right = Width; int res = DrawTextW (hdcDib, &cc, 1, &rect, DT_LEFT | DT_TOP); POINT point; point.y = res; int w = res; // BOOL rey = GetCharWidth32 (hdcDib, (uint8) cc, (uint8) cc, &w); BOOL rey = GetCharWidth32 (hdcDib, cc, cc, &w); nlassert (rey); point.x = w; // ABC abc; // BOOL rey = GetCharABCWidths (hdcDib, (uint8) cc, (uint8) cc, &abc); // nlassert (rey); // point.x = abc.abcA; SIZE s; GetTextExtentPoint32W (hdcDib, &cc, 1, &s); BOOL ret = LPtoDP (hdcDib, &point, 1); nlassert (ret); static uint8 buf[100*100]; sint32 _top = 0, _left = point.x; sint32 right = 0, bottom = point.y; bool emptyLine; for (sint y = 0; y < point.y; y++) { emptyLine = true; for (sint x = 0; x < point.x; x++) { buf[y*100+x] = (uint8) Buffer[y*100+x]; if (buf[y*100+x]) { emptyLine = false; if (x < _left) _left = x; if (x > right) right = x; } // printf (buf[y*100+x]?"*":"."); } if (!emptyLine) { if (_top == 0) _top = y; bottom = y; } // printf ("\n"); } width = right - _left + 1; if (right - _left + 1 < 0) width = 0; height = bottom - _top + 1; if (bottom - _top + 1 < 0) height = 0; pitch = 100; advx = point.x; WORD ag = 0; /*uint t = GetGlyphIndices (hdcDib, &cc, 1, &ag, 0); if (t == GDI_ERROR) { nlstop; } */ glyphIndex = ag; top = -_top; left = -_left; /* { for (sint y = _top; y < _top + height; y++) { for (sint x = _left; x < _left + width; x++) { printf (buf[y*100+x]?"*":"."); } printf ("\n"); } printf ("w: %d h: %d s: %d a: %d l: %d t: %d", width, height, size, advx, left, top); getchar(); } */ _top = _left = top = left = 0; top = s.cy; width = s.cx; height = s.cy; return buf + _top * 100 + _left; } void CFontGenerator::getKerning (ucchar left, ucchar right, sint32 &kernx) { /* if (!FT_HAS_KERNING(_Face)) { kernx = 0; } else { FT_Vector kerning; FT_Error error = FT_Get_Kerning (_Face, left, right, ft_kerning_default, &kerning); if (error) { nlerror ("FT_Get_Kerning() failed: %s", getFT2Error(error)); } kernx = kerning.x; }*/ } uint32 CFontGenerator::getCharIndex (ucchar c) { // return FT_Get_Char_Index (_Face, c); return 0; } } // NL3D #endif // NL_DONT_USE_EXTERNAL_CODE