First version of glyph page loading supporting for freetype stk

Performance should be good, it cleans some unused code too.

Windows build not tested.
This commit is contained in:
Benau 2015-10-08 16:41:31 +08:00
parent 1e7e895d7f
commit ebeca35460
14 changed files with 281 additions and 179 deletions

View File

@ -68,8 +68,6 @@ public:
//! The texture and the corresponding rectangle and sprite will all be added to the end of each array.
//! returns the index of the sprite or -1 on failure
virtual s32 addTextureAsSprite(video::ITexture* texture) = 0;
//! Use for cropping freetype glyph bitmap correctly.
virtual s32 addTextureAsSprite(video::ITexture* texture, s32 width, s32 height) = 0;
//! clears sprites, rectangles and textures
virtual void clear() = 0;

View File

@ -131,31 +131,6 @@ s32 CGUISpriteBank::addTextureAsSprite(video::ITexture* texture)
return Sprites.size() - 1;
}
//! Use for cropping freetype glyph bitmap correctly.
s32 CGUISpriteBank::addTextureAsSprite(video::ITexture* texture, s32 width, s32 height)
{
if ( !texture )
return -1;
addTexture(texture);
u32 textureIndex = getTextureCount() - 1;
u32 rectangleIndex = Rectangles.size();
Rectangles.push_back( core::rect<s32>(0,0, width, height) );
SGUISprite sprite;
sprite.frameTime = 0;
SGUISpriteFrame frame;
frame.textureNumber = textureIndex;
frame.rectNumber = rectangleIndex;
sprite.Frames.push_back( frame );
Sprites.push_back( sprite );
return Sprites.size() - 1;
}
//! draws a sprite in 2d with scale and color
void CGUISpriteBank::draw2DSprite(u32 index, const core::position2di& pos,
const core::rect<s32>* clip, const video::SColor& color,

View File

@ -42,8 +42,6 @@ public:
//! Add the texture and use it for a single non-animated sprite.
virtual s32 addTextureAsSprite(video::ITexture* texture);
//! Use for cropping freetype glyph bitmap correctly.
virtual s32 addTextureAsSprite(video::ITexture* texture, s32 width, s32 height);
//! clears sprites, rectangles and textures
virtual void clear();

View File

@ -167,34 +167,6 @@ s32 STKModifiedSpriteBank::addTextureAsSprite(video::ITexture* texture)
return Sprites.size() - 1;
} // addTextureAsSprite
// ----------------------------------------------------------------------------
/** Use for cropping freetype glyph bitmap correctly.
*/
s32 STKModifiedSpriteBank::addTextureAsSprite(video::ITexture* texture, s32 width, s32 height)
{
assert( m_magic_number == 0xCAFEC001 );
if ( !texture )
return -1;
addTexture(texture);
u32 textureIndex = getTextureCount() - 1;
u32 rectangleIndex = Rectangles.size();
Rectangles.push_back( core::rect<s32>(0,0, width, height) );
SGUISprite sprite;
sprite.frameTime = 0;
SGUISpriteFrame frame;
frame.textureNumber = textureIndex;
frame.rectNumber = rectangleIndex;
sprite.Frames.push_back( frame );
Sprites.push_back( sprite );
return Sprites.size() - 1;
} // addTextureAsSprite
// ----------------------------------------------------------------------------
//! draws a sprite in 2d with scale and color
void STKModifiedSpriteBank::draw2DSprite(u32 index,

View File

@ -45,8 +45,6 @@ public:
//! Add the texture and use it for a single non-animated sprite.
virtual s32 addTextureAsSprite(video::ITexture* texture);
//! Use for cropping freetype glyph bitmap correctly.
virtual s32 addTextureAsSprite(video::ITexture* texture, s32 width, s32 height);
//! clears sprites, rectangles and textures
virtual void clear();

View File

@ -1,6 +1,4 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009-2010 John Norman
// Copyright (C) 2015 Ben Au
//
// This program is free software; you can redistribute it and/or
@ -16,16 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// The image loading function is partially based on CGUITTFont.cpp by John Norman,
// original version is located here:
//
// http://irrlicht.suckerfreegames.com/
#ifdef ENABLE_FREETYPE
#include "guiengine/TTF_handling.hpp"
#include "graphics/irr_driver.hpp"
#include "io/file_manager.hpp"
#include "utils/translation.hpp"
#include <algorithm>
@ -133,75 +125,6 @@ void loadBoldChar(TTFfile* ttf_file, float scale)
}
}
video::IImage* generateTTFImage(FT_Bitmap bits, video::IVideoDriver* Driver)
{
core::dimension2du d(bits.width + 1, bits.rows + 1);
core::dimension2du texture_size;
video::IImage* image = 0;
switch (bits.pixel_mode)
{
case FT_PIXEL_MODE_MONO:
{
// Create a blank image and fill it with transparent pixels.
texture_size = d.getOptimalSize(true, true);
image = Driver->createImage(video::ECF_A1R5G5B5, texture_size);
image->fill(video::SColor(0, 255, 255, 255));
// Load the monochrome data in.
const u32 image_pitch = image->getPitch() / sizeof(u16);
u16* image_data = (u16*)image->lock();
u8* glyph_data = bits.buffer;
for (u32 y = 0; y < bits.rows; ++y)
{
u16* row = image_data;
for (u32 x = 0; x < bits.width; ++x)
{
// Monochrome bitmaps store 8 pixels per byte. The left-most pixel is the bit 0x80.
// So, we go through the data each bit at a time.
if ((glyph_data[y * bits.pitch + (x / 8)] & (0x80 >> (x % 8))) != 0)
*row = 0xFFFF;
++row;
}
image_data += image_pitch;
}
image->unlock();
break;
}
case FT_PIXEL_MODE_GRAY:
{
// Create our blank image.
texture_size = d.getOptimalSize(true, true, true, 0); //Enable force power of 2 texture size to gain performance,
//But may increase vram usage???
image = Driver->createImage(video::ECF_A8R8G8B8, texture_size);
image->fill(video::SColor(0, 255, 255, 255));
// Load the grayscale data in.
const float gray_count = static_cast<float>(bits.num_grays);
const u32 image_pitch = image->getPitch() / sizeof(u32);
u32* image_data = (u32*)image->lock();
u8* glyph_data = bits.buffer;
for (u32 y = 0; y < bits.rows; ++y)
{
u8* row = glyph_data;
for (u32 x = 0; x < bits.width; ++x)
{
image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
//data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
}
glyph_data += bits.pitch;
}
image->unlock();
break;
}
default:
Log::error("ScalableFont::loadTTF", "Freetype failed to create bitmap!!");
return 0;
}
return image;
}
} // end namespace gui
} // end namespace irr
#endif // ENABLE_FREETYPE

View File

@ -1,4 +1,3 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2015 Ben Au
//
@ -16,7 +15,7 @@
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <IVideoDriver.h>
#include <irrlicht.h>
#include <ft2build.h>
#include FT_FREETYPE_H
@ -38,7 +37,6 @@ typedef struct
}TTFfile;
TTFfile getTTFAndChar(const core::stringc &langname, TTFLoadingType, FontUse&);
video::IImage* generateTTFImage (FT_Bitmap, video::IVideoDriver*);
void loadChar(core::stringc, TTFfile*, FontUse&, float);
void loadNumber(TTFfile*, float);
void loadBoldChar(TTFfile*, float);

View File

@ -690,6 +690,7 @@ namespace GUIEngine
Skin* g_skin = NULL;
#ifdef ENABLE_FREETYPE
Ft_Env* g_ft_env = NULL;
GlyphPageCreator* g_gp_creator = NULL;
#endif // ENABLE_FREETYPE
ScalableFont *g_font;
ScalableFont *g_outline_font;
@ -958,6 +959,8 @@ namespace GUIEngine
#ifdef ENABLE_FREETYPE
g_ft_env->~Ft_Env();
g_ft_env = NULL;
g_gp_creator->~GlyphPageCreator();
g_gp_creator = NULL;
#endif // ENABLE_FREETYPE
for (unsigned int i=0; i<g_loaded_screens.size(); i++)
@ -1030,6 +1033,7 @@ namespace GUIEngine
#ifdef ENABLE_FREETYPE
g_ft_env = new Ft_Env();
g_gp_creator = new GlyphPageCreator();
#endif // ENABLE_FREETYPE
/*

View File

@ -41,6 +41,7 @@ namespace irr
#ifdef ENABLE_FREETYPE
#include "guiengine/freetype_environment.hpp"
#include "guiengine/glyph_page_creator.hpp"
#endif // ENABLE_FREETYPE
/**
@ -87,6 +88,7 @@ namespace GUIEngine
extern Skin* g_skin;
#ifdef ENABLE_FREETYPE
extern irr::gui::Ft_Env* g_ft_env;
extern irr::gui::GlyphPageCreator* g_gp_creator;
#endif // ENABLE_FREETYPE
extern irr::gui::ScalableFont* g_small_font;
extern irr::gui::ScalableFont* g_font;
@ -184,7 +186,13 @@ namespace GUIEngine
* \pre GUIEngine::init must have been called first
* \return the freetype and library with face
*/
inline irr::gui::Ft_Env* get_Freetype() { return Private::g_ft_env; }
inline irr::gui::Ft_Env* get_Freetype() { return Private::g_ft_env; }
/**
* \pre GUIEngine::init must have been called first
* \return the glyph page creator, useful to create a glyph page from individual char
*/
inline irr::gui::GlyphPageCreator* get_GP_Creator() { return Private::g_gp_creator; }
#endif // ENABLE_FREETYPE
Screen* getScreenNamed(const char* name);

View File

@ -1,4 +1,3 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2015 Ben Au
//
@ -15,6 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifdef ENABLE_FREETYPE
#include "guiengine/freetype_environment.hpp"
#include "guiengine/TTF_handling.hpp"

View File

@ -1,4 +1,3 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2015 Ben Au
//

View File

@ -0,0 +1,147 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2009-2010 John Norman
// Copyright (C) 2015 Ben Au
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// The image loading function is partially based on CGUITTFont.cpp by John Norman,
// original version is located here:
//
// http://irrlicht.suckerfreegames.com/
#ifdef ENABLE_FREETYPE
#include <irrlicht.h>
#include "guiengine/engine.hpp"
namespace irr
{
namespace gui
{
GlyphPageCreator::GlyphPageCreator()
{
page = GUIEngine::getDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(512, 512));
}
GlyphPageCreator::~GlyphPageCreator()
{
clearGlyphPage();
page->drop();
page = 0;
}
bool GlyphPageCreator::checkEnoughSpace(FT_Bitmap bits)
{
core::dimension2du d(bits.width + 1, bits.rows + 1);
core::dimension2du texture_size;
texture_size = d.getOptimalSize(!(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NPOT)),
!(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NSQUARE)), true, 0);
if (used_width + texture_size.Width > 512 && used_height + temp_height + texture_size.Height > 512)
return false;
return true;
}
void GlyphPageCreator::clearGlyphPage()
{
used_width = 0;
temp_height = 0;
used_height = 0;
}
void GlyphPageCreator::createNewGlyphPage()
{
video::IImage* blank = GUIEngine::getDriver()->createImage(video::ECF_A8R8G8B8, core::dimension2du(512, 512));
blank->copyTo(page, core::position2di(0, 0));
blank->drop();
blank = 0;
}
video::IImage* GlyphPageCreator::getPage()
{
return page;
}
bool GlyphPageCreator::insertGlyph(FT_Bitmap bits, core::rect<s32>& rect)
{
core::dimension2du d(bits.width + 1, bits.rows + 1);
core::dimension2du texture_size;
switch (bits.pixel_mode)
{
case FT_PIXEL_MODE_GRAY:
{
// Create our blank image.
texture_size = d.getOptimalSize(!(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NPOT)),
!(GUIEngine::getDriver()->queryFeature(video::EVDF_TEXTURE_NSQUARE)), true, 0);
image = GUIEngine::getDriver()->createImage(video::ECF_A8R8G8B8, texture_size);
image->fill(video::SColor(0, 255, 255, 255));
// Load the grayscale data in.
const float gray_count = static_cast<float>(bits.num_grays);
const u32 image_pitch = image->getPitch() / sizeof(u32);
u32* image_data = (u32*)image->lock();
u8* glyph_data = bits.buffer;
for (u32 y = 0; y < bits.rows; ++y)
{
u8* row = glyph_data;
for (u32 x = 0; x < bits.width; ++x)
{
image_data[y * image_pitch + x] |= static_cast<u32>(255.0f * (static_cast<float>(*row++) / gray_count)) << 24;
//data[y * image_pitch + x] |= ((u32)(*bitsdata++) << 24);
}
glyph_data += bits.pitch;
}
image->unlock();
break;
}
default:
return false;
}
if (!image)
return false;
//Done creating a single glyph, now copy to the glyph page...
//Determine the linebreak location
if (used_width + texture_size.Width > 512)
{
used_width = 0;
used_height += temp_height;
temp_height = 0;
}
//Copy now
image->copyTo(page, core::position2di(used_width, used_height));
//Store the rectangle of current glyph
rect = core::rect<s32> (used_width, used_height, used_width + bits.width, used_height + bits.rows);
image = 0;
//Store used area
used_width += texture_size.Width;
if (temp_height < texture_size.Height)
temp_height = texture_size.Height;
return true;
}
u32 GlyphPageCreator::used_width = 0;
u32 GlyphPageCreator::used_height = 0;
u32 GlyphPageCreator::temp_height = 0;
video::IImage* GlyphPageCreator::page = 0;
} // end namespace gui
} // end namespace irr
#endif // ENABLE_FREETYPE

View File

@ -0,0 +1,49 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2015 Ben Au
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <IVideoDriver.h>
#include <irrlicht.h>
#include <ft2build.h>
#include FT_FREETYPE_H
namespace irr
{
namespace gui
{
class GlyphPageCreator
{
public:
GlyphPageCreator();
~GlyphPageCreator();
static bool checkEnoughSpace(FT_Bitmap);
static void clearGlyphPage();
static void createNewGlyphPage();
static video::IImage* getPage();
bool insertGlyph(FT_Bitmap, core::rect<s32>&);
private:
video::IImage* image = 0;
static video::IImage* page;
static u32 temp_height;
static u32 used_width;
static u32 used_height;
};
} // end namespace gui
} // end namespace irr

View File

@ -21,6 +21,7 @@
#include <cwctype>
#define cur_face GUIEngine::get_Freetype()->ft_face[fu]
#define gp_creator GUIEngine::get_GP_Creator()
namespace irr
{
@ -392,49 +393,81 @@ bool ScalableFont::loadTTF()
std::set<wchar_t>::iterator it;
s32 current_maxheight = 0;
s32 t;
u32 texno = 0;
SpriteBank->addTexture(NULL);
gp_creator->createNewGlyphPage();
for (it = TTF_file.usedchar.begin(); it != TTF_file.usedchar.end(); ++it)
{
SGUISpriteFrame f;
SGUISprite s;
core::rect<s32> rectangle;
// Retrieve glyph index from character code, skip useless glyph.
int idx = FT_Get_Char_Index(cur_face, *it);
if (idx == 0) continue;
if (idx)
{
// Load glyph image into the slot (erase previous one)
err = FT_Load_Glyph(cur_face, idx,
FT_LOAD_DEFAULT | FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL);
if (err)
Log::error("ScalableFont::loadTTF", "Can't load a single glyph.");
// Load glyph image into the slot (erase previous one)
err = FT_Load_Glyph(cur_face, idx,
FT_LOAD_DEFAULT | FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL);
if (err)
Log::error("ScalableFont::loadTTF", "Can't load a single glyph.");
// Store vertical offset on line.
s32 offset_on_line = (cur_face->glyph->metrics.height >> 6) - cur_face->glyph->bitmap_top;
offset.push_back(offset_on_line);
// Store vertical offset on line.
s32 offset_on_line = (cur_face->glyph->metrics.height >> 6) - cur_face->glyph->bitmap_top;
offset.push_back(offset_on_line);
// This is to be used later.
t = cur_face->glyph->metrics.height >> 6;
height.push_back(t);
if (t > current_maxheight)
current_maxheight = t;
//This is to be used later.
t = cur_face->glyph->metrics.height >> 6;
height.push_back(t);
if (t > current_maxheight)
current_maxheight = t;
// Store horizontal padding (bearingX).
s32 bX = cur_face->glyph->bitmap_left;
bx.push_back(bX);
// Store horizontal padding (bearingX).
s32 bX = cur_face->glyph->bitmap_left;
bx.push_back(bX);
// Store total width on horizontal line.
s32 width = cur_face->glyph->advance.x >> 6;
advance.push_back(width);
// Store total width on horizontal line.
s32 width = cur_face->glyph->advance.x >> 6;
advance.push_back(width);
// Convert to an anti-aliased bitmap
FT_Bitmap bits = slot->bitmap;
// Convert to an anti-aliased bitmap
FT_Bitmap bits = slot->bitmap;
CharacterMap[*it] = SpriteBank->getSprites().size();
//Generate image of glyph
video::IImage* image = generateTTFImage(bits, Driver);
if (!image)
return false;
if (!gp_creator->checkEnoughSpace(bits))
// Glyph page is full, save current one and reset the current page
{
SpriteBank->setTexture(texno, Driver->addTexture("Glyph_page", gp_creator->getPage()));
gp_creator->clearGlyphPage();
SpriteBank->addTexture(NULL);
gp_creator->createNewGlyphPage();
texno++;
}
//Add it as texture
SpriteBank->addTextureAsSprite(Driver->addTexture("character",image),
cur_face->glyph->metrics.width >> 6, t);
image->drop();
CharacterMap[*it] = SpriteBank->getSprites().size() - 1;
// Inserting now
if (gp_creator->insertGlyph(bits, rectangle))
{
f.rectNumber = SpriteBank->getPositions().size();
f.textureNumber = texno;
// add frame to sprite
s.Frames.push_back(f);
s.frameTime = 0;
SpriteBank->getPositions().push_back(rectangle);
SpriteBank->getSprites().push_back(s);
}
else
return false;
}
// Check for glyph page which can fit all characters
if (it == --TTF_file.usedchar.end())
{
SpriteBank->setTexture(texno, Driver->addTexture("Glyph_page", gp_creator->getPage()));
gp_creator->clearGlyphPage();
}
}
//Fix unused glyphs....
@ -510,18 +543,18 @@ bool ScalableFont::loadTTF()
{
case Normal:
Log::info("ScalableFont::loadTTF", "Created %d glyphs "
"supporting %d characters for normal font %s at %d dpi."
, Areas.size(), CharacterMap.size(), cur_face->family_name, TTF_file.size);
"supporting %d characters for normal font %s at %d dpi using %d glyph page(s)."
, Areas.size(), CharacterMap.size(), cur_face->family_name, TTF_file.size, texno + 1);
break;
case Digit:
Log::info("ScalableFont::loadTTF", "Created %d glyphs "
"supporting %d characters for high-res digits font %s at %d dpi."
, Areas.size(), CharacterMap.size(), cur_face->family_name, TTF_file.size);
"supporting %d characters for high-res digits font %s at %d dpi using %d glyph page(s)."
, Areas.size(), CharacterMap.size(), cur_face->family_name, TTF_file.size, texno + 1);
break;
case Bold:
Log::info("ScalableFont::loadTTF", "Created %d glyphs "
"supporting %d characters for bold title font %s at %d dpi."
, Areas.size(), CharacterMap.size(), cur_face->family_name, TTF_file.size);
"supporting %d characters for bold title font %s at %d dpi using %d glyph page(s)."
, Areas.size(), CharacterMap.size(), cur_face->family_name, TTF_file.size, texno + 1);
break;
}