bgfx/examples/common/font/font_manager.cpp

766 lines
20 KiB
C++
Raw Normal View History

2013-05-15 09:07:04 -04:00
/*
* Copyright 2013 Jeremie Roy. All rights reserved.
2013-04-22 16:42:11 -04:00
* License: http://www.opensource.org/licenses/BSD-2-Clause
2013-05-15 09:07:04 -04:00
*/
2013-05-19 01:12:40 -04:00
#include "../common.h"
2013-05-17 01:03:57 -04:00
#include <bgfx.h>
#include <freetype/freetype.h>
#include <edtaa3/edtaa3func.cpp>
2013-05-15 22:42:39 -04:00
#include <wchar.h> // wcslen
#include "font_manager.h"
#include "../cube_atlas.h"
2013-04-22 16:42:11 -04:00
#if BGFX_CONFIG_USE_TINYSTL
namespace tinystl
{
//struct bgfx_allocator
//{
2013-05-15 09:21:23 -04:00
//static void* static_allocate(size_t _bytes);
//static void static_deallocate(void* _ptr, size_t /*_bytes*/);
2013-04-22 16:42:11 -04:00
//};
} // namespace tinystl
//# define TINYSTL_ALLOCATOR tinystl::bgfx_allocator
2013-05-15 09:21:23 -04:00
# include <TINYSTL/unordered_map.h>
2013-04-22 16:42:11 -04:00
//# include <TINYSTL/unordered_set.h>
namespace stl = tinystl;
#else
2013-05-15 09:21:23 -04:00
# include <unordered_map>
namespace std
{ namespace tr1
2013-05-15 22:42:39 -04:00
{}
2013-05-15 09:21:23 -04:00
}
namespace stl
{
2013-04-22 16:42:11 -04:00
using namespace std;
using namespace std::tr1;
}
#endif // BGFX_CONFIG_USE_TINYSTL
2013-05-15 22:42:39 -04:00
struct FTHolder
{
FT_Library library;
FT_Face face;
};
2013-05-16 23:54:25 -04:00
class TrueTypeFont
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
public:
2013-05-15 22:42:39 -04:00
TrueTypeFont();
~TrueTypeFont();
/// Initialize from an external buffer
/// @remark The ownership of the buffer is external, and you must ensure it stays valid up to this object lifetime
/// @return true if the initialization succeed
bool init(const uint8_t* _buffer, uint32_t _bufferSize, int32_t _fontIndex, uint32_t _pixelHeight);
/// return the font descriptor of the current font
FontInfo getFontInfo();
/// raster a glyph as 8bit alpha to a memory buffer
/// update the GlyphInfo according to the raster strategy
/// @ remark buffer min size: glyphInfo.m_width * glyphInfo * height * sizeof(char)
2013-05-17 01:03:57 -04:00
bool bakeGlyphAlpha(CodePoint _codePoint, GlyphInfo& _outGlyphInfo, uint8_t* _outBuffer);
2013-05-15 22:42:39 -04:00
/// raster a glyph as 32bit subpixel rgba to a memory buffer
/// update the GlyphInfo according to the raster strategy
/// @ remark buffer min size: glyphInfo.m_width * glyphInfo * height * sizeof(uint32_t)
2013-05-17 01:03:57 -04:00
bool bakeGlyphSubpixel(CodePoint _codePoint, GlyphInfo& _outGlyphInfo, uint8_t* _outBuffer);
2013-05-15 22:42:39 -04:00
/// raster a glyph as 8bit signed distance to a memory buffer
/// update the GlyphInfo according to the raster strategy
/// @ remark buffer min size: glyphInfo.m_width * glyphInfo * height * sizeof(char)
2013-05-17 01:03:57 -04:00
bool bakeGlyphDistance(CodePoint _codePoint, GlyphInfo& _outGlyphInfo, uint8_t* _outBuffer);
2013-05-16 23:54:25 -04:00
2013-04-22 16:42:11 -04:00
private:
2013-05-15 22:42:39 -04:00
FTHolder* m_font;
2013-04-22 16:42:11 -04:00
};
2013-05-16 23:54:25 -04:00
TrueTypeFont::TrueTypeFont() : m_font(NULL)
2013-05-15 09:21:23 -04:00
{
2013-04-22 16:42:11 -04:00
}
2013-05-16 23:54:25 -04:00
TrueTypeFont::~TrueTypeFont()
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
if (m_font != NULL)
2013-04-22 16:42:11 -04:00
{
FTHolder* holder = (FTHolder*) m_font;
2013-05-15 09:21:23 -04:00
FT_Done_Face(holder->face);
FT_Done_FreeType(holder->library);
2013-04-22 16:42:11 -04:00
delete m_font;
m_font = NULL;
}
}
2013-05-16 23:54:25 -04:00
bool TrueTypeFont::init(const uint8_t* _buffer, uint32_t _bufferSize, int32_t _fontIndex, uint32_t _pixelHeight)
2013-04-22 16:42:11 -04:00
{
2013-05-17 01:03:57 -04:00
BX_CHECK( (_bufferSize > 256 && _bufferSize < 100000000), "TrueType buffer size is suspicious");
BX_CHECK( (_pixelHeight > 4 && _pixelHeight < 128), "TrueType buffer size is suspicious");
2013-05-15 09:21:23 -04:00
BX_CHECK(m_font == NULL, "TrueTypeFont already initialized");
FTHolder* holder = new FTHolder();
2013-04-22 16:42:11 -04:00
// Initialize Freetype library
2013-05-15 09:21:23 -04:00
FT_Error error = FT_Init_FreeType(&holder->library);
if (error)
2013-04-22 16:42:11 -04:00
{
delete holder;
return false;
}
2013-05-15 09:21:23 -04:00
error = FT_New_Memory_Face(holder->library, _buffer, _bufferSize, _fontIndex, &holder->face);
if (error == FT_Err_Unknown_File_Format)
{
2013-04-22 16:42:11 -04:00
// the font file could be opened and read, but it appears
//that its font format is unsupported
2013-05-15 09:21:23 -04:00
FT_Done_FreeType(holder->library);
2013-04-22 16:42:11 -04:00
delete holder;
return false;
}
2013-05-15 09:21:23 -04:00
else if (error)
2013-04-22 16:42:11 -04:00
{
// another error code means that the font file could not
// be opened or read, or simply that it is broken...
2013-05-15 09:21:23 -04:00
FT_Done_FreeType(holder->library);
2013-04-22 16:42:11 -04:00
delete holder;
return false;
}
2013-05-15 09:21:23 -04:00
// Select unicode charmap
error = FT_Select_Charmap(holder->face, FT_ENCODING_UNICODE);
if (error)
{
FT_Done_Face(holder->face);
FT_Done_FreeType(holder->library);
return false;
}
2013-04-22 16:42:11 -04:00
//set size in pixels
2013-05-15 09:21:23 -04:00
error = FT_Set_Pixel_Sizes(holder->face, 0, _pixelHeight);
if (error)
{
FT_Done_Face(holder->face);
FT_Done_FreeType(holder->library);
return false;
}
2013-04-22 16:42:11 -04:00
m_font = holder;
return true;
}
2013-05-16 23:54:25 -04:00
FontInfo TrueTypeFont::getFontInfo()
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
BX_CHECK(m_font != NULL, "TrueTypeFont not initialized");
2013-04-22 16:42:11 -04:00
FTHolder* holder = (FTHolder*) m_font;
2013-05-15 09:21:23 -04:00
BX_CHECK(FT_IS_SCALABLE(holder->face), "Font is unscalable");
2013-04-22 16:42:11 -04:00
FT_Size_Metrics metrics = holder->face->size->metrics;
2013-04-22 16:42:11 -04:00
FontInfo outFontInfo;
outFontInfo.scale = 1.0f;
2013-05-15 09:21:23 -04:00
outFontInfo.ascender = metrics.ascender / 64.0f;
outFontInfo.descender = metrics.descender / 64.0f;
outFontInfo.lineGap = (metrics.height - metrics.ascender + metrics.descender) / 64.0f;
2013-05-22 11:13:17 -04:00
outFontInfo.maxAdvanceWidth = metrics.max_advance/ 64.0f;
2013-05-15 09:21:23 -04:00
2013-05-16 23:54:25 -04:00
outFontInfo.underlinePosition = FT_MulFix(holder->face->underline_position, metrics.y_scale) / 64.0f;
outFontInfo.underlineThickness = FT_MulFix(holder->face->underline_thickness, metrics.y_scale) / 64.0f;
2013-04-22 16:42:11 -04:00
return outFontInfo;
}
2013-05-17 01:03:57 -04:00
bool TrueTypeFont::bakeGlyphAlpha(CodePoint _codePoint, GlyphInfo& _glyphInfo, uint8_t* _outBuffer)
2013-05-15 09:21:23 -04:00
{
BX_CHECK(m_font != NULL, "TrueTypeFont not initialized");
2013-04-22 16:42:11 -04:00
FTHolder* holder = (FTHolder*) m_font;
2013-05-15 09:21:23 -04:00
_glyphInfo.glyphIndex = FT_Get_Char_Index(holder->face, _codePoint);
FT_GlyphSlot slot = holder->face->glyph;
2013-05-15 09:21:23 -04:00
FT_Error error = FT_Load_Glyph(holder->face, _glyphInfo.glyphIndex, FT_LOAD_DEFAULT);
if (error)
{
return false;
}
2013-04-22 16:42:11 -04:00
FT_Glyph glyph;
2013-05-15 09:21:23 -04:00
error = FT_Get_Glyph(slot, &glyph);
if (error)
{
return false;
}
error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
if (error)
{
return false;
}
2013-04-22 16:42:11 -04:00
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
2013-05-15 09:21:23 -04:00
int32_t x = bitmap->left;
int32_t y = -bitmap->top;
int32_t w = bitmap->bitmap.width;
int32_t h = bitmap->bitmap.rows;
2013-04-22 16:42:11 -04:00
_glyphInfo.offset_x = (float) x;
2013-05-15 09:21:23 -04:00
_glyphInfo.offset_y = (float) y;
_glyphInfo.width = (float) w;
_glyphInfo.height = (float) h;
_glyphInfo.advance_x = (float)slot->advance.x / 64.0f;
_glyphInfo.advance_y = (float)slot->advance.y / 64.0f;
2013-04-22 16:42:11 -04:00
int32_t charsize = 1;
2013-05-15 09:21:23 -04:00
int32_t depth = 1;
int32_t stride = bitmap->bitmap.pitch;
2013-05-15 09:21:23 -04:00
for (int32_t ii = 0; ii < h; ++ii)
{
memcpy(_outBuffer + (ii * w) * charsize * depth,
2013-05-15 22:42:39 -04:00
bitmap->bitmap.buffer + (ii * stride) * charsize, w * charsize * depth);
2013-05-15 09:21:23 -04:00
}
2013-04-22 16:42:11 -04:00
FT_Done_Glyph(glyph);
return true;
}
2013-05-17 01:03:57 -04:00
bool TrueTypeFont::bakeGlyphSubpixel(CodePoint _codePoint, GlyphInfo& _glyphInfo, uint8_t* _outBuffer)
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
BX_CHECK(m_font != NULL, "TrueTypeFont not initialized");
2013-04-22 16:42:11 -04:00
FTHolder* holder = (FTHolder*) m_font;
2013-05-15 09:21:23 -04:00
_glyphInfo.glyphIndex = FT_Get_Char_Index(holder->face, _codePoint);
FT_GlyphSlot slot = holder->face->glyph;
2013-05-15 09:21:23 -04:00
FT_Error error = FT_Load_Glyph(holder->face, _glyphInfo.glyphIndex, FT_LOAD_DEFAULT);
if (error)
{
return false;
}
2013-04-22 16:42:11 -04:00
FT_Glyph glyph;
2013-05-15 09:21:23 -04:00
error = FT_Get_Glyph(slot, &glyph);
if (error)
{
return false;
}
error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_LCD, 0, 1);
if (error)
{
return false;
}
2013-04-22 16:42:11 -04:00
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
int32_t x = bitmap->left;
int32_t y = -bitmap->top;
int32_t w = bitmap->bitmap.width;
int32_t h = bitmap->bitmap.rows;
2013-04-22 16:42:11 -04:00
_glyphInfo.offset_x = (float) x;
2013-05-15 09:21:23 -04:00
_glyphInfo.offset_y = (float) y;
_glyphInfo.width = (float) w;
_glyphInfo.height = (float) h;
_glyphInfo.advance_x = (float)slot->advance.x / 64.0f;
_glyphInfo.advance_y = (float)slot->advance.y / 64.0f;
int32_t charsize = 1;
2013-05-15 09:21:23 -04:00
int32_t depth = 3;
int32_t stride = bitmap->bitmap.pitch;
2013-05-15 09:21:23 -04:00
for (int32_t ii = 0; ii < h; ++ii)
{
memcpy(_outBuffer + (ii * w) * charsize * depth,
2013-05-15 22:42:39 -04:00
bitmap->bitmap.buffer + (ii * stride) * charsize, w * charsize * depth);
2013-05-15 09:21:23 -04:00
}
2013-04-22 16:42:11 -04:00
FT_Done_Glyph(glyph);
return true;
}
2013-05-17 01:03:57 -04:00
void make_distance_map(uint8_t* _img, uint8_t* _outImg, uint32_t _width, uint32_t _height)
2013-04-22 16:42:11 -04:00
{
2013-05-17 01:03:57 -04:00
int16_t* xdist = (int16_t*)malloc(_width * _height * sizeof(int16_t) );
int16_t* ydist = (int16_t*)malloc(_width * _height * sizeof(int16_t) );
double* gx = (double*)calloc(_width * _height, sizeof(double) );
double* gy = (double*)calloc(_width * _height, sizeof(double) );
double* data = (double*)calloc(_width * _height, sizeof(double) );
double* outside = (double*)calloc(_width * _height, sizeof(double) );
double* inside = (double*)calloc(_width * _height, sizeof(double) );
2013-05-15 09:21:23 -04:00
uint32_t ii;
// Convert img into double (data)
double img_min = 255, img_max = -255;
2013-05-17 01:03:57 -04:00
for (ii = 0; ii < _width * _height; ++ii)
2013-05-15 09:21:23 -04:00
{
2013-05-17 01:03:57 -04:00
double v = _img[ii];
2013-05-15 09:21:23 -04:00
data[ii] = v;
if (v > img_max)
{
img_max = v;
}
if (v < img_min)
{
img_min = v;
}
}
// Rescale image levels between 0 and 1
2013-05-17 01:03:57 -04:00
for (ii = 0; ii < _width * _height; ++ii)
2013-05-15 09:21:23 -04:00
{
2013-05-17 01:03:57 -04:00
data[ii] = (_img[ii] - img_min) / (img_max - img_min);
2013-05-15 09:21:23 -04:00
}
// Compute outside = edtaa3(bitmap); % Transform background (0's)
2013-05-17 01:03:57 -04:00
computegradient(data, _width, _height, gx, gy);
edtaa3(data, gx, gy, _width, _height, xdist, ydist, outside);
for (ii = 0; ii < _width * _height; ++ii)
2013-05-15 09:21:23 -04:00
{
if (outside[ii] < 0)
{
outside[ii] = 0.0;
}
}
// Compute inside = edtaa3(1-bitmap); % Transform foreground (1's)
2013-05-17 01:03:57 -04:00
memset(gx, 0, sizeof(double) * _width * _height);
memset(gy, 0, sizeof(double) * _width * _height);
for (ii = 0; ii < _width * _height; ++ii)
2013-05-15 09:21:23 -04:00
{
data[ii] = 1.0 - data[ii];
}
2013-05-17 01:03:57 -04:00
computegradient(data, _width, _height, gx, gy);
edtaa3(data, gx, gy, _width, _height, xdist, ydist, inside);
for (ii = 0; ii < _width * _height; ++ii)
2013-05-15 09:21:23 -04:00
{
if (inside[ii] < 0)
{
inside[ii] = 0.0;
}
}
// distmap = outside - inside; % Bipolar distance field
2013-05-17 01:03:57 -04:00
uint8_t* out = _outImg;
for (ii = 0; ii < _width * _height; ++ii)
2013-05-15 09:21:23 -04:00
{
2013-04-23 16:48:34 -04:00
outside[ii] -= inside[ii];
2013-05-15 09:21:23 -04:00
outside[ii] = 128 + outside[ii] * 16;
2013-04-22 16:42:11 -04:00
2013-05-15 09:21:23 -04:00
if (outside[ii] < 0)
{
outside[ii] = 0;
}
if (outside[ii] > 255)
{
outside[ii] = 255;
}
2013-05-17 01:03:57 -04:00
out[ii] = 255 - (uint8_t) outside[ii];
2013-05-15 09:21:23 -04:00
}
free(xdist);
free(ydist);
free(gx);
free(gy);
free(data);
free(outside);
free(inside);
}
2013-04-22 16:42:11 -04:00
2013-05-17 01:03:57 -04:00
bool TrueTypeFont::bakeGlyphDistance(CodePoint _codePoint, GlyphInfo& _glyphInfo, uint8_t* _outBuffer)
2013-05-15 09:21:23 -04:00
{
BX_CHECK(m_font != NULL, "TrueTypeFont not initialized");
2013-04-22 16:42:11 -04:00
FTHolder* holder = (FTHolder*) m_font;
2013-05-15 09:21:23 -04:00
_glyphInfo.glyphIndex = FT_Get_Char_Index(holder->face, _codePoint);
FT_Int32 loadMode = FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING;
2013-04-22 16:42:11 -04:00
FT_Render_Mode renderMode = FT_RENDER_MODE_NORMAL;
FT_GlyphSlot slot = holder->face->glyph;
2013-05-15 09:21:23 -04:00
FT_Error error = FT_Load_Glyph(holder->face, _glyphInfo.glyphIndex, loadMode);
if (error)
{
return false;
}
2013-04-22 16:42:11 -04:00
FT_Glyph glyph;
2013-05-15 09:21:23 -04:00
error = FT_Get_Glyph(slot, &glyph);
if (error)
{
return false;
}
error = FT_Glyph_To_Bitmap(&glyph, renderMode, 0, 1);
if (error)
{
return false;
}
2013-04-22 16:42:11 -04:00
FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph;
2013-05-15 09:21:23 -04:00
int32_t x = bitmap->left;
int32_t y = -bitmap->top;
int32_t w = bitmap->bitmap.width;
int32_t h = bitmap->bitmap.rows;
2013-04-22 16:42:11 -04:00
_glyphInfo.offset_x = (float) x;
2013-05-15 09:21:23 -04:00
_glyphInfo.offset_y = (float) y;
_glyphInfo.width = (float) w;
_glyphInfo.height = (float) h;
_glyphInfo.advance_x = (float)slot->advance.x / 64.0f;
_glyphInfo.advance_y = (float)slot->advance.y / 64.0f;
int32_t charsize = 1;
2013-05-15 09:21:23 -04:00
int32_t depth = 1;
int32_t stride = bitmap->bitmap.pitch;
2013-04-22 16:42:11 -04:00
2013-05-15 09:21:23 -04:00
for (int32_t ii = 0; ii < h; ++ii)
{
memcpy(_outBuffer + (ii * w) * charsize * depth,
2013-05-15 22:42:39 -04:00
bitmap->bitmap.buffer + (ii * stride) * charsize, w * charsize * depth);
2013-05-15 09:21:23 -04:00
}
2013-04-22 16:42:11 -04:00
FT_Done_Glyph(glyph);
2013-05-15 09:21:23 -04:00
if (w * h > 0)
2013-04-22 16:42:11 -04:00
{
uint32_t dw = 6;
2013-05-15 09:21:23 -04:00
uint32_t dh = 6;
if (dw < 2)
{
dw = 2;
}
if (dh < 2)
{
dh = 2;
}
uint32_t nw = w + dw * 2;
uint32_t nh = h + dh * 2;
BX_CHECK(nw * nh < 128 * 128, "buffer overflow");
uint32_t buffSize = nw * nh * sizeof(uint8_t);
uint8_t* alphaImg = (uint8_t*) malloc(buffSize);
memset(alphaImg, 0, nw * nh * sizeof(uint8_t) );
2013-04-22 16:42:11 -04:00
//copy the original buffer to the temp one
2013-05-15 09:21:23 -04:00
for (uint32_t ii = dh; ii < nh - dh; ++ii)
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
memcpy(alphaImg + ii * nw + dw, _outBuffer + (ii - dh) * w, w);
2013-04-22 16:42:11 -04:00
}
2013-05-15 09:21:23 -04:00
make_distance_map(alphaImg, _outBuffer, nw, nh);
2013-05-15 09:21:23 -04:00
free(alphaImg);
_glyphInfo.offset_x -= (float) dw;
_glyphInfo.offset_y -= (float) dh;
2013-05-15 09:21:23 -04:00
_glyphInfo.width = (float) nw;
_glyphInfo.height = (float) nh;
2013-04-22 16:42:11 -04:00
}
2013-05-15 09:21:23 -04:00
return true;
}
2013-04-22 16:42:11 -04:00
2013-05-17 01:03:57 -04:00
typedef stl::unordered_map<CodePoint, GlyphInfo> GlyphHashMap;
2013-04-22 16:42:11 -04:00
// cache font data
struct FontManager::CachedFont
{
2013-05-15 09:21:23 -04:00
CachedFont()
{
trueTypeFont = NULL; masterFontHandle.idx = -1;
}
FontInfo fontInfo;
2013-05-17 01:03:57 -04:00
GlyphHashMap cachedGlyphs;
2013-05-16 23:54:25 -04:00
TrueTypeFont* trueTypeFont;
2013-04-22 16:42:11 -04:00
// an handle to a master font in case of sub distance field font
2013-05-15 09:21:23 -04:00
FontHandle masterFontHandle;
int16_t padding;
2013-04-22 16:42:11 -04:00
};
const uint16_t MAX_OPENED_FILES = 64;
const uint16_t MAX_OPENED_FONT = 64;
2013-05-15 09:21:23 -04:00
const uint32_t MAX_FONT_BUFFER_SIZE = 512 * 512 * 4;
2013-04-22 16:42:11 -04:00
2013-05-15 22:42:39 -04:00
FontManager::FontManager(Atlas* _atlas)
: m_ownAtlas(false)
, m_atlas(_atlas)
, m_fontHandles(MAX_OPENED_FONT)
, m_filesHandles(MAX_OPENED_FILES)
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
init();
2013-04-22 16:42:11 -04:00
}
2013-05-15 22:42:39 -04:00
FontManager::FontManager(uint32_t _textureSideWidth)
: m_ownAtlas(true)
, m_atlas(new Atlas(_textureSideWidth) )
, m_fontHandles(MAX_OPENED_FONT)
, m_filesHandles(MAX_OPENED_FILES)
2013-04-22 16:42:11 -04:00
{
init();
}
void FontManager::init()
{
m_cachedFiles = new CachedFile[MAX_OPENED_FILES];
m_cachedFonts = new CachedFont[MAX_OPENED_FONT];
m_buffer = new uint8_t[MAX_FONT_BUFFER_SIZE];
2013-05-15 09:21:23 -04:00
2013-05-08 13:53:21 -04:00
const uint32_t W = 3;
2013-04-22 16:42:11 -04:00
// Create filler rectangle
2013-05-15 09:21:23 -04:00
uint8_t buffer[W * W * 4];
memset(buffer, 255, W * W * 4);
2013-05-08 13:53:21 -04:00
m_blackGlyph.width = W;
m_blackGlyph.height = W;
2013-05-08 13:53:21 -04:00
///make sure the black glyph doesn't bleed by using a one pixel inner outline
2013-05-15 09:21:23 -04:00
m_blackGlyph.regionIndex = m_atlas->addRegion(W, W, buffer, AtlasRegion::TYPE_GRAY, 1);
2013-04-22 16:42:11 -04:00
}
FontManager::~FontManager()
{
2013-04-23 17:14:32 -04:00
BX_CHECK(m_fontHandles.getNumHandles() == 0, "All the fonts must be destroyed before destroying the manager");
2013-05-15 09:21:23 -04:00
delete[] m_cachedFonts;
2013-04-22 16:42:11 -04:00
2013-04-23 17:14:32 -04:00
BX_CHECK(m_filesHandles.getNumHandles() == 0, "All the font files must be destroyed before destroying the manager");
2013-05-15 09:21:23 -04:00
delete[] m_cachedFiles;
delete[] m_buffer;
if (m_ownAtlas)
{
2013-04-22 16:42:11 -04:00
delete m_atlas;
}
}
2013-05-30 00:53:19 -04:00
TrueTypeHandle FontManager::createTtf(const uint8_t* _buffer, uint32_t _size)
2013-05-15 09:21:23 -04:00
{
uint16_t id = m_filesHandles.alloc();
2013-04-23 17:14:32 -04:00
BX_CHECK(id != bx::HandleAlloc::invalid, "Invalid handle used");
m_cachedFiles[id].buffer = new uint8_t[_size];
m_cachedFiles[id].bufferSize = _size;
memcpy(m_cachedFiles[id].buffer, _buffer, _size);
2013-05-15 09:21:23 -04:00
2013-04-22 16:42:11 -04:00
TrueTypeHandle ret = {id};
return ret;
}
2013-05-30 00:53:19 -04:00
void FontManager::destroyTtf(TrueTypeHandle _handle)
2013-04-22 16:42:11 -04:00
{
2013-04-23 17:14:32 -04:00
BX_CHECK(bgfx::invalidHandle != _handle.idx, "Invalid handle used");
delete m_cachedFiles[_handle.idx].buffer;
m_cachedFiles[_handle.idx].bufferSize = 0;
m_cachedFiles[_handle.idx].buffer = NULL;
m_filesHandles.free(_handle.idx);
2013-04-22 16:42:11 -04:00
}
2013-05-17 01:03:57 -04:00
FontHandle FontManager::createFontByPixelSize(TrueTypeHandle _tt_handle, uint32_t _typefaceIndex, uint32_t _pixelSize, uint32_t _fontType)
2013-04-22 16:42:11 -04:00
{
2013-04-23 17:14:32 -04:00
BX_CHECK(bgfx::invalidHandle != _tt_handle.idx, "Invalid handle used");
2013-04-22 16:42:11 -04:00
TrueTypeFont* ttf = new TrueTypeFont();
2013-05-15 09:21:23 -04:00
if (!ttf->init(m_cachedFiles[_tt_handle.idx].buffer, m_cachedFiles[_tt_handle.idx].bufferSize, _typefaceIndex, _pixelSize) )
2013-04-22 16:42:11 -04:00
{
delete ttf;
FontHandle invalid = BGFX_INVALID_HANDLE;
return invalid;
}
2013-05-15 09:21:23 -04:00
2013-04-22 16:42:11 -04:00
uint16_t fontIdx = m_fontHandles.alloc();
2013-05-15 09:21:23 -04:00
BX_CHECK(fontIdx != bx::HandleAlloc::invalid, "Invalid handle used");
m_cachedFonts[fontIdx].trueTypeFont = ttf;
m_cachedFonts[fontIdx].fontInfo = ttf->getFontInfo();
2013-05-15 09:21:23 -04:00
m_cachedFonts[fontIdx].fontInfo.fontType = _fontType;
m_cachedFonts[fontIdx].fontInfo.pixelSize = _pixelSize;
m_cachedFonts[fontIdx].cachedGlyphs.clear();
m_cachedFonts[fontIdx].masterFontHandle.idx = -1;
2013-04-22 16:42:11 -04:00
FontHandle ret = {fontIdx};
return ret;
}
FontHandle FontManager::createScaledFontToPixelSize(FontHandle _baseFontHandle, uint32_t _pixelSize)
{
2013-04-23 17:14:32 -04:00
BX_CHECK(bgfx::invalidHandle != _baseFontHandle.idx, "Invalid handle used");
2013-04-22 16:42:11 -04:00
CachedFont& font = m_cachedFonts[_baseFontHandle.idx];
FontInfo& fontInfo = font.fontInfo;
2013-04-22 16:42:11 -04:00
FontInfo newFontInfo = fontInfo;
newFontInfo.pixelSize = _pixelSize;
newFontInfo.scale = (float)_pixelSize / (float) fontInfo.pixelSize;
newFontInfo.ascender = (newFontInfo.ascender * newFontInfo.scale);
newFontInfo.descender = (newFontInfo.descender * newFontInfo.scale);
newFontInfo.lineGap = (newFontInfo.lineGap * newFontInfo.scale);
2013-05-22 11:13:17 -04:00
newFontInfo.maxAdvanceWidth = (newFontInfo.maxAdvanceWidth * newFontInfo.scale);
2013-05-16 23:54:25 -04:00
newFontInfo.underlineThickness = (newFontInfo.underlineThickness * newFontInfo.scale);
newFontInfo.underlinePosition = (newFontInfo.underlinePosition * newFontInfo.scale);
2013-04-22 16:42:11 -04:00
uint16_t fontIdx = m_fontHandles.alloc();
2013-04-23 17:14:32 -04:00
BX_CHECK(fontIdx != bx::HandleAlloc::invalid, "Invalid handle used");
m_cachedFonts[fontIdx].cachedGlyphs.clear();
m_cachedFonts[fontIdx].fontInfo = newFontInfo;
m_cachedFonts[fontIdx].trueTypeFont = NULL;
m_cachedFonts[fontIdx].masterFontHandle = _baseFontHandle;
2013-04-22 16:42:11 -04:00
FontHandle ret = {fontIdx};
return ret;
}
void FontManager::destroyFont(FontHandle _handle)
{
2013-04-23 17:14:32 -04:00
BX_CHECK(bgfx::invalidHandle != _handle.idx, "Invalid handle used");
2013-04-22 16:42:11 -04:00
2013-05-15 09:21:23 -04:00
if (m_cachedFonts[_handle.idx].trueTypeFont != NULL)
2013-04-22 16:42:11 -04:00
{
delete m_cachedFonts[_handle.idx].trueTypeFont;
m_cachedFonts[_handle.idx].trueTypeFont = NULL;
2013-04-22 16:42:11 -04:00
}
2013-05-15 09:21:23 -04:00
m_cachedFonts[_handle.idx].cachedGlyphs.clear();
2013-04-22 16:42:11 -04:00
m_fontHandles.free(_handle.idx);
}
bool FontManager::preloadGlyph(FontHandle _handle, const wchar_t* _string)
2013-05-15 09:21:23 -04:00
{
2013-04-23 17:14:32 -04:00
BX_CHECK(bgfx::invalidHandle != _handle.idx, "Invalid handle used");
CachedFont& font = m_cachedFonts[_handle.idx];
2013-04-22 16:42:11 -04:00
//if truetype present
2013-05-15 09:21:23 -04:00
if (font.trueTypeFont != NULL)
{
2013-04-22 16:42:11 -04:00
//parse string
2013-05-15 09:21:23 -04:00
for (uint32_t ii = 0, end = wcslen(_string); ii < end; ++ii)
2013-04-22 16:42:11 -04:00
{
//if glyph cached, continue
2013-05-17 01:03:57 -04:00
CodePoint codePoint = _string[ii];
2013-05-15 09:21:23 -04:00
if (!preloadGlyph(_handle, codePoint) )
2013-04-22 16:42:11 -04:00
{
return false;
}
}
2013-05-15 09:21:23 -04:00
2013-04-22 16:42:11 -04:00
return true;
}
return false;
}
2013-05-17 01:03:57 -04:00
bool FontManager::preloadGlyph(FontHandle _handle, CodePoint _codePoint)
2013-04-22 16:42:11 -04:00
{
2013-04-23 17:14:32 -04:00
BX_CHECK(bgfx::invalidHandle != _handle.idx, "Invalid handle used");
CachedFont& font = m_cachedFonts[_handle.idx];
FontInfo& fontInfo = font.fontInfo;
2013-04-22 16:42:11 -04:00
//check if glyph not already present
2013-05-17 01:03:57 -04:00
GlyphHashMap::iterator iter = font.cachedGlyphs.find(_codePoint);
2013-05-15 09:21:23 -04:00
if (iter != font.cachedGlyphs.end() )
2013-04-22 16:42:11 -04:00
{
return true;
}
//if truetype present
2013-05-15 09:21:23 -04:00
if (font.trueTypeFont != NULL)
2013-04-22 16:42:11 -04:00
{
GlyphInfo glyphInfo;
2013-05-15 09:21:23 -04:00
2013-04-22 16:42:11 -04:00
//bake glyph as bitmap to buffer
2013-05-15 09:21:23 -04:00
switch (font.fontInfo.fontType)
2013-04-22 16:42:11 -04:00
{
case FONT_TYPE_ALPHA:
font.trueTypeFont->bakeGlyphAlpha(_codePoint, glyphInfo, m_buffer);
2013-04-22 16:42:11 -04:00
break;
2013-05-15 09:21:23 -04:00
2013-05-15 22:42:39 -04:00
//case FONT_TYPE_LCD:
//font.m_trueTypeFont->bakeGlyphSubpixel(codePoint, glyphInfo, m_buffer);
//break;
2013-04-22 16:42:11 -04:00
case FONT_TYPE_DISTANCE:
font.trueTypeFont->bakeGlyphDistance(_codePoint, glyphInfo, m_buffer);
2013-04-22 16:42:11 -04:00
break;
2013-05-15 09:21:23 -04:00
2013-04-22 16:42:11 -04:00
case FONT_TYPE_DISTANCE_SUBPIXEL:
font.trueTypeFont->bakeGlyphDistance(_codePoint, glyphInfo, m_buffer);
2013-04-22 16:42:11 -04:00
break;
2013-05-15 09:21:23 -04:00
2013-04-22 16:42:11 -04:00
default:
2013-04-23 17:14:32 -04:00
BX_CHECK(false, "TextureType not supported yet");
2013-05-15 09:21:23 -04:00
}
2013-04-22 16:42:11 -04:00
//copy bitmap to texture
2013-05-15 09:21:23 -04:00
if (!addBitmap(glyphInfo, m_buffer) )
2013-04-22 16:42:11 -04:00
{
return false;
}
glyphInfo.advance_x = (glyphInfo.advance_x * fontInfo.scale);
glyphInfo.advance_y = (glyphInfo.advance_y * fontInfo.scale);
glyphInfo.offset_x = (glyphInfo.offset_x * fontInfo.scale);
glyphInfo.offset_y = (glyphInfo.offset_y * fontInfo.scale);
glyphInfo.height = (glyphInfo.height * fontInfo.scale);
2013-05-15 09:21:23 -04:00
glyphInfo.width = (glyphInfo.width * fontInfo.scale);
2013-04-22 16:42:11 -04:00
// store cached glyph
font.cachedGlyphs[_codePoint] = glyphInfo;
2013-04-22 16:42:11 -04:00
return true;
2013-05-15 09:21:23 -04:00
}
else
2013-04-22 16:42:11 -04:00
{
//retrieve glyph from parent font if any
2013-05-15 09:21:23 -04:00
if (font.masterFontHandle.idx != bgfx::invalidHandle)
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
if (preloadGlyph(font.masterFontHandle, _codePoint) )
2013-04-22 16:42:11 -04:00
{
2013-05-30 00:53:19 -04:00
const GlyphInfo* glyph = getGlyphInfo(font.masterFontHandle, _codePoint);
2013-04-22 16:42:11 -04:00
2013-05-30 00:53:19 -04:00
GlyphInfo glyphInfo = *glyph;
glyphInfo.advance_x = (glyphInfo.advance_x * fontInfo.scale);
glyphInfo.advance_y = (glyphInfo.advance_y * fontInfo.scale);
glyphInfo.offset_x = (glyphInfo.offset_x * fontInfo.scale);
glyphInfo.offset_y = (glyphInfo.offset_y * fontInfo.scale);
glyphInfo.height = (glyphInfo.height * fontInfo.scale);
glyphInfo.width = (glyphInfo.width * fontInfo.scale);
2013-04-22 16:42:11 -04:00
// store cached glyph
font.cachedGlyphs[_codePoint] = glyphInfo;
2013-04-22 16:42:11 -04:00
return true;
}
}
}
return false;
}
2013-05-22 11:13:17 -04:00
const FontInfo& FontManager::getFontInfo(FontHandle _handle) const
2013-05-15 09:21:23 -04:00
{
2013-04-23 17:14:32 -04:00
BX_CHECK(bgfx::invalidHandle != _handle.idx, "Invalid handle used");
return m_cachedFonts[_handle.idx].fontInfo;
2013-04-22 16:42:11 -04:00
}
2013-05-30 00:53:19 -04:00
const GlyphInfo* FontManager::getGlyphInfo(FontHandle _handle, CodePoint _codePoint)
2013-05-15 09:21:23 -04:00
{
2013-05-30 00:53:19 -04:00
const GlyphHashMap& cachedGlyphs = m_cachedFonts[_handle.idx].cachedGlyphs;
GlyphHashMap::const_iterator it = cachedGlyphs.find(_codePoint);
if (it == cachedGlyphs.end() )
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
if (preloadGlyph(_handle, _codePoint) )
2013-04-22 16:42:11 -04:00
{
2013-05-30 00:53:19 -04:00
it = cachedGlyphs.find(_codePoint);
2013-05-15 09:21:23 -04:00
}
else
2013-04-22 16:42:11 -04:00
{
2013-05-30 00:53:19 -04:00
return NULL;
2013-04-22 16:42:11 -04:00
}
}
2013-05-15 09:21:23 -04:00
2013-05-30 00:53:19 -04:00
return &it->second;
2013-04-22 16:42:11 -04:00
}
bool FontManager::addBitmap(GlyphInfo& _glyphInfo, const uint8_t* _data)
2013-04-22 16:42:11 -04:00
{
2013-05-15 09:21:23 -04:00
_glyphInfo.regionIndex = m_atlas->addRegion( (uint16_t) ceil(_glyphInfo.width), (uint16_t) ceil(_glyphInfo.height), _data, AtlasRegion::TYPE_GRAY);
2013-04-22 16:42:11 -04:00
return true;
}