[DEV] Test distance field developpement

This commit is contained in:
Edouard DUPIN 2014-01-07 21:33:46 +01:00
parent 3e617536af
commit 9c6fe98018
8 changed files with 509 additions and 2 deletions

2
external/egami vendored

@ -1 +1 @@
Subproject commit 20ac6565ca545f15249fc9d492e2aed175624fad
Subproject commit cd88edc7085edbef3d78bee02104c697a0713057

View File

@ -0,0 +1,357 @@
/**
* @author Edouard DUPIN
*
* @copyright 2011, Edouard DUPIN, all right reserved
*
* @license BSD v3 (see license file)
*/
#include <etk/types.h>
#include <etk/os/FSNode.h>
#include <egami/egami.h>
#include <ewol/resource/Manager.h>
#include <ewol/resource/font/FontBase.h>
#include <ewol/resource/TexturedFont.h>
#include <ewol/resource/FontFreeType.h>
#include <ewol/context/Context.h>
#include <ewol/resource/DistanceFieldFont.h>
#undef __class__
#define __class__ "resource::DistanceFieldFont"
ewol::resource::DistanceFieldFont::DistanceFieldFont(const std::string& _fontName) :
ewol::resource::Texture(_fontName) {
addObjectType("ewol::resource::DistanceFieldFont");
m_font = NULL;
m_lastGlyphPos.setValue(1,1);
m_lastRawHeigh = 0;
m_size = 15;
std::string localName = _fontName;
std::vector<std::string> folderList;
if (true == ewol::getContext().getFontDefault().getUseExternal()) {
#if defined(__TARGET_OS__Android)
folderList.push_back("/system/fonts");
#elif defined(__TARGET_OS__Linux)
folderList.push_back("/usr/share/fonts/truetype");
#endif
}
folderList.push_back(ewol::getContext().getFontDefault().getFolder());
for (size_t folderID = 0; folderID < folderList.size() ; folderID++) {
etk::FSNode myFolder(folderList[folderID]);
// find the real Font name :
std::vector<std::string> output;
myFolder.folderGetRecursiveFiles(output);
std::vector<std::string> split = std::split(localName, ';');
EWOL_INFO("try to find font named : " << split << " in: " << myFolder);
//EWOL_CRITICAL("parse string : " << split);
bool hasFindAFont = false;
for (size_t jjj=0; jjj<split.size(); jjj++) {
EWOL_INFO(" try with : '" << split[jjj] << "'");
for (size_t iii=0; iii<output.size(); iii++) {
//EWOL_DEBUG(" file : " << output[iii]);
if( true == end_with(output[iii], split[jjj]+"-"+"regular"+".ttf", false)
|| true == end_with(output[iii], split[jjj]+"-"+"r"+".ttf", false)
|| true == end_with(output[iii], split[jjj]+"regular"+".ttf", false)
|| true == end_with(output[iii], split[jjj]+"r"+".ttf", false)
|| true == end_with(output[iii], split[jjj]+".ttf", false)) {
EWOL_INFO(" find Font [Regular] : " << output[iii]);
m_fileName = output[iii];
hasFindAFont=true;
break;
}
}
if (hasFindAFont == true) {
EWOL_INFO(" find this font : '" << split[jjj] << "'");
break;
} else if (jjj == split.size()-1) {
EWOL_ERROR("Find NO font in the LIST ... " << split);
}
}
if (hasFindAFont == true) {
EWOL_INFO(" find this font : '" << folderList[folderID] << "'");
break;
} else if (folderID == folderList.size()-1) {
EWOL_ERROR("Find NO font in the LIST ... " << folderList);
}
}
if (m_fileName.size() == 0) {
EWOL_ERROR("can not load FONT name : '" << m_fileName << "' ==> size=" << m_size );
m_font = NULL;
return;
}
EWOL_INFO("Load FONT name : '" << m_fileName << "' ==> size=" << m_size);
m_font = ewol::resource::FontFreeType::keep(m_fileName);
if (m_font == NULL) {
EWOL_ERROR("Pb Loading FONT name : '" << m_fileName << "' ==> size=" << m_size );
}
// set the bassic charset:
m_listElement.clear();
if (m_font == NULL) {
return;
}
m_height = m_font->getHeight(m_size);
// TODO : basic font use 512 is better ... == > maybe estimate it with the dpi ???
setImageSize(ivec2(512,32));
// now we can acces directly on the image
m_data.clear(etk::Color<>(0x00000000));
// add error glyph
addGlyph(0);
// by default we set only the first AINSI char availlable
for (int32_t iii=0x20; iii<0x7F; iii++) {
addGlyph(iii);
}
flush();
}
ewol::resource::DistanceFieldFont::~DistanceFieldFont(void) {
ewol::resource::FontFreeType::release(m_font);
}
class GirdDF {
private:
std::vector<ivec2> m_data;
ivec2 m_size;
public:
GirdDF(const ivec2& _size, const ivec2& _base = ivec2(0,0)) {
m_size = _size;
m_data.resize(m_size.x()*m_size.y(), _base);
}
const ivec2& get(const ivec2& _pos) const {
static const ivec2 error(0, 0);
if( _pos.x()>0 && _pos.x()<m_size.x()
&& _pos.y()>0 && _pos.y()<m_size.y()) {
return m_data[_pos.x()+_pos.y()*m_size.x()];
}
return error;
}
void set(const ivec2& _pos, const ivec2& _data) {
if( _pos.x()>0 && _pos.x()<m_size.x()
&& _pos.y()>0 && _pos.y()<m_size.y()) {
m_data[_pos.x()+_pos.y()*m_size.x()] = _data;
}
}
void compare(ivec2& _data, ivec2 _pos, ivec2 _offset) {
ivec2 other = get(_pos + _offset) + _offset;
if (other.length2() < _data.length2()) {
_data = other;
}
}
void GenerateSoftDistanceField(void) {
// First pass :
for (int32_t yyy = 0; yyy < m_size.y(); ++yyy) {
for (int32_t xxx = 0; xxx < m_size.x(); ++xxx) {
ivec2 data = get(ivec2(xxx, yyy));
compare(data, ivec2(xxx, yyy), ivec2(-1, 0));
compare(data, ivec2(xxx, yyy), ivec2( 0, -1));
compare(data, ivec2(xxx, yyy), ivec2(-1, -1));
compare(data, ivec2(xxx, yyy), ivec2( 1, -1));
set(ivec2(xxx, yyy), data);
}
for (int32_t xxx = m_size.y()-1; xxx >= 0;--xxx) {
ivec2 data = get(ivec2(xxx, yyy));
compare(data, ivec2(xxx, yyy), ivec2(1, 0));
set(ivec2(xxx, yyy), data );
}
}
// Second pass
for (int32_t yyy = m_size.y()-1; yyy >= 0; --yyy) {
for (int32_t xxx = m_size.x()-1; xxx >= 0; --xxx) {
ivec2 data = get(ivec2(xxx, yyy));
compare(data, ivec2(xxx, yyy), ivec2( 1, 0));
compare(data, ivec2(xxx, yyy), ivec2( 0, 1));
compare(data, ivec2(xxx, yyy), ivec2(-1, 1));
compare(data, ivec2(xxx, yyy), ivec2( 1, 1));
set(ivec2(xxx, yyy), data );
}
for (int32_t xxx = 0; xxx < m_size.x(); ++xxx) {
ivec2 data = get(ivec2(xxx, yyy));
compare(data, ivec2(xxx, yyy), ivec2(-1, 0));
set(ivec2(xxx, yyy), data);
}
}
}
};
void ewol::resource::DistanceFieldFont::GenerateDistanceField(egami::ImageMono _input, egami::Image _output) {
GirdDF myGird1(_input.getSize());
GirdDF myGird2(_input.getSize());
// Reformat gird :
for (int32_t xxx = 0; xxx < _input.getSize().x(); ++xxx) {
for (int32_t yyy = 0; yyy < _input.getSize().y(); ++yyy) {
if ( _input.get(ivec2(xxx, yyy)) < 128 ) {
myGird1.set(ivec2(xxx, yyy), ivec2(0, 0));
myGird2.set(ivec2(xxx, yyy), ivec2(9999, 9999));
} else {
myGird1.set(ivec2(xxx, yyy), ivec2(9999, 9999));
myGird2.set(ivec2(xxx, yyy), ivec2(0, 0));
}
}
}
// Create internal distance of the 2 layer mode :
myGird1.GenerateSoftDistanceField();
myGird2.GenerateSoftDistanceField();
// Generate output :
_output.resize(_input.getSize(), etk::Color<>(0));
_output.clear(etk::Color<>(0));
for (int32_t xxx = 0; xxx < _output.getSize().x(); ++xxx) {
for (int32_t yyy = 0; yyy < _output.getSize().y(); ++yyy) {
float dist1 = myGird1.get(ivec2(xxx, yyy)).length();
float dist2 = myGird2.get(ivec2(xxx, yyy)).length();
float dist = dist1 - dist2;
float value = etk_avg(0.0f, dist*3.0f + 128.0f, 256.0f);
_output.set(ivec2(xxx, yyy), etk::Color<>((int32_t)value,(int32_t)value,(int32_t)value,255));
}
}
}
bool ewol::resource::DistanceFieldFont::addGlyph(const char32_t& _val) {
bool hasChange = false;
if (m_font == NULL) {
return false;
}
// add the curent "char"
GlyphProperty tmpchar;
tmpchar.m_UVal = _val;
egami::ImageMono imageGlyphRaw;
egami::Image imageGlyphDistanceField;
EWOL_DEBUG("Generate Glyph : " << _val);
if (m_font->getGlyphProperty(m_size, tmpchar) == true) {
//EWOL_DEBUG("load char : '" << _val << "'=" << _val.get());
hasChange = true;
// change line if needed ...
if (m_lastGlyphPos.x() + tmpchar.m_sizeTexture.x() > m_data.getSize().x()) {
m_lastGlyphPos.setX(1);
m_lastGlyphPos += ivec2(0, m_lastRawHeigh);
m_lastRawHeigh = 0;
}
while(m_lastGlyphPos.y()+tmpchar.m_sizeTexture.y() > m_data.getSize().y()) {
ivec2 size = m_data.getSize();
size.setY(size.y()*2);
m_data.resize(size, etk::Color<>(0));
// change the coordonate on the element in the texture
for (size_t jjj = 0; jjj < m_listElement.size(); ++jjj) {
m_listElement[jjj].m_texturePosStart *= vec2(1.0f, 0.5f);
m_listElement[jjj].m_texturePosSize *= vec2(1.0f, 0.5f);
}
}
// draw the glyph
m_font->drawGlyph(imageGlyphRaw, m_size*10, tmpchar);
GenerateDistanceField(imageGlyphRaw, imageGlyphDistanceField);
m_data.insert(m_lastGlyphPos, imageGlyphDistanceField);
// set image position
tmpchar.m_texturePosStart.setValue( (float)m_lastGlyphPos.x() / (float)m_data.getSize().x(),
(float)m_lastGlyphPos.y() / (float)m_data.getSize().y() );
tmpchar.m_texturePosSize.setValue( (float)tmpchar.m_sizeTexture.x() / (float)m_data.getSize().x(),
(float)tmpchar.m_sizeTexture.y() / (float)m_data.getSize().y() );
// update the maximum of the line hight :
if (m_lastRawHeigh < tmpchar.m_sizeTexture.y()) {
// note : +1 is for the overlapping of the glyph (Part 2)
m_lastRawHeigh = tmpchar.m_sizeTexture.y()+1;
}
// note : +1 is for the overlapping of the glyph (Part 3)
// update the Bitmap position drawing :
m_lastGlyphPos += ivec2(tmpchar.m_sizeTexture.x()+1, 0);
} else {
EWOL_WARNING("Did not find char : '" << _val << "'=" << _val);
tmpchar.setNotExist();
}
m_listElement.push_back(tmpchar);
//m_font[iii]->display();
// generate the kerning for all the characters :
if (tmpchar.exist() == true) {
// TODO : set the kerning back ...
//m_font[iii]->generateKerning(m_size, m_listElement[iii]);
}
if (hasChange == true) {
flush();
//egami::store(m_data, "fileFont.bmp"); // ==> for debug test only ...
}
return hasChange;
}
int32_t ewol::resource::DistanceFieldFont::getIndex(char32_t _charcode) {
if (_charcode < 0x20) {
return 0;
} else if (_charcode < 0x80) {
return _charcode - 0x1F;
} else {
for (size_t iii=0x80-0x20; iii < m_listElement.size(); iii++) {
//EWOL_DEBUG("search : '" << charcode << "' =?= '" << (m_listElement[displayMode])[iii].m_UVal << "'");
if (_charcode == (m_listElement)[iii].m_UVal) {
//EWOL_DEBUG("search : '" << charcode << "'");
if ((m_listElement)[iii].exist()) {
//EWOL_DEBUG("return " << iii);
return iii;
} else {
return 0;
}
}
}
}
if (addGlyph(_charcode) == true) {
// TODO : This does not work due to the fact that the update of open GL is not done in the context main cycle !!!
ewol::getContext().forceRedrawAll();
}
return 0;
}
ewol::GlyphProperty* ewol::resource::DistanceFieldFont::getGlyphPointer(const char32_t& _charcode) {
//EWOL_DEBUG("Get glyph property for mode: " << _displayMode << " == > wrapping index : " << m_modeWraping[_displayMode]);
int32_t index = getIndex(_charcode);
if( index < 0
|| (size_t)index >= m_listElement.size() ) {
EWOL_ERROR(" Try to get glyph index inexistant ... == > return the index 0 ... id=" << index);
if (m_listElement.size() > 0) {
return &((m_listElement)[0]);
}
return NULL;
}
//EWOL_ERROR(" index=" << index);
//EWOL_ERROR(" m_UVal=" << m_listElement[_displayMode][index].m_UVal);
//EWOL_ERROR(" m_glyphIndex=" << m_listElement[_displayMode][index].m_glyphIndex);
//EWOL_ERROR(" m_advance=" << m_listElement[_displayMode][index].m_advance);
//EWOL_ERROR(" m_bearing=" << m_listElement[_displayMode][index].m_bearing);
return &((m_listElement)[index]);
}
ewol::resource::DistanceFieldFont* ewol::resource::DistanceFieldFont::keep(const std::string& _filename) {
EWOL_VERBOSE("KEEP : DistanceFieldFont : file : '" << _filename << "'");
ewol::resource::DistanceFieldFont* object = static_cast<ewol::resource::DistanceFieldFont*>(getManager().localKeep(_filename));
if (NULL != object) {
return object;
}
// need to crate a new one ...
EWOL_DEBUG("CREATE: DistanceFieldFont : file : '" << _filename << "'");
object = new ewol::resource::DistanceFieldFont(_filename);
if (NULL == object) {
EWOL_ERROR("allocation error of a resource : " << _filename);
return NULL;
}
getManager().localAdd(object);
return object;
}
void ewol::resource::DistanceFieldFont::release(ewol::resource::DistanceFieldFont*& _object) {
if (NULL == _object) {
return;
}
std::string name = _object->getName();
int32_t count = _object->getCounter() - 1;
ewol::Resource* object2 = static_cast<ewol::Resource*>(_object);
if (getManager().release(object2) == true) {
EWOL_DEBUG("REMOVE: DistanceFieldFont : file : '" << name << "' count=" << count);
//etk::displayBacktrace(false);
}
_object = NULL;
}

View File

@ -0,0 +1,95 @@
/**
* @author Edouard DUPIN
*
* @copyright 2011, Edouard DUPIN, all right reserved
*
* @license BSD v3 (see license file)
*/
#ifndef __EWOL_DISTANCE_FIELD_FONT_H__
#define __EWOL_DISTANCE_FIELD_FONT_H__
#include <ewol/resource/font/FontBase.h>
#include <ewol/resource/Texture.h>
#include <ewol/resource/Resource.h>
#include <ewol/resource/TexturedFont.h>
namespace ewol {
namespace resource {
class DistanceFieldFont : public ewol::resource::Texture {
private:
std::string m_fileName;
int32_t m_size;
int32_t m_height;
// specific element to have the the know if the specify element is known...
// == > otherwise I can just generate italic ...
// == > Bold is a little more complicated (maybe with the bordersize)
ewol::resource::FontBase* m_font;
public:
std::vector<GlyphProperty> m_listElement;
private:
// for the texture generation :
ivec2 m_lastGlyphPos;
int32_t m_lastRawHeigh;
protected:
DistanceFieldFont(const std::string& _fontName);
~DistanceFieldFont(void);
public:
/**
* @brief get the display height of this font
* @param[in] _displayMode Mode to display the currrent font
* @return Dimention of the font need between 2 lines
*/
// TODO : Might be deprecated ... ==> no size for this font ...
int32_t getHeight(const enum ewol::font::mode _displayMode = ewol::font::Regular) {
return m_height;
};
/**
* @brief get the font height (user friendly)
* @return Dimention of the font the user requested
*/
// TODO : Might be deprecated ... ==> no size for this font ...
int32_t getFontSize(void) {
return m_size;
};
// TODO : Need to convert a text size in a real size and this oposite ...
/**
* @brief get the ID of a unicode charcode
* @param[in] _charcode The unicodeValue
* @return The ID in the table (if it does not exist : return 0)
*/
int32_t getIndex(char32_t _charcode);
/**
* @brief get the pointer on the coresponding glyph
* @param[in] _charcode The unicodeValue
* @return The pointer on the glyph == > never NULL
*/
ewol::GlyphProperty* getGlyphPointer(const char32_t& _charcode);
public:
/**
* @brief keep the resource pointer.
* @note Never free this pointer by your own...
* @param[in] _filename Name of the texture font.
* @return pointer on the resource or NULL if an error occured.
*/
static ewol::resource::DistanceFieldFont* keep(const std::string& _filename);
/**
* @brief release the keeped resources
* @param[in,out] reference on the object pointer
*/
static void release(ewol::resource::DistanceFieldFont*& _object);
private:
/**
* @brief add a glyph in a texture font.
* @param[in] _val Char value to add.
* @return true if the image size have change, false otherwise
*/
bool addGlyph(const char32_t& _val);
void GenerateDistanceField(egami::ImageMono _input, egami::Image _output);
};
};
};
#endif

View File

@ -222,6 +222,50 @@ bool ewol::resource::FontFreeType::drawGlyph(egami::Image& _imageOut,
return true;
}
bool ewol::resource::FontFreeType::drawGlyph(egami::ImageMono& _imageOut,
int32_t _fontSize,
ewol::GlyphProperty& _property) {
if(false == m_init) {
return false;
}
// 300dpi (hight quality) 96 dpi (normal quality)
int32_t fontQuality = 96;
// Select size ...
// note tha <<6 == *64 corespond with the 1/64th of points calculation of freetype
int32_t error = FT_Set_Char_Size(m_fftFace, _fontSize<<6, _fontSize<<6, fontQuality, fontQuality);
if (0!=error ) {
EWOL_ERROR("FT_Set_Char_Size == > error in settings ...");
return false;
}
// a small shortcut
FT_GlyphSlot slot = m_fftFace->glyph;
// load glyph image into the slot (erase previous one)
error = FT_Load_Glyph(m_fftFace, // handle to face object
_property.m_glyphIndex, // glyph index
FT_LOAD_DEFAULT );
if (0!=error ) {
EWOL_ERROR("FT_Load_Glyph specify Glyph");
return false;
}
// convert to an anti-aliased bitmap
error = FT_Render_Glyph(slot, FT_RENDER_MODE_NORMAL ); // TODO : set FT_RENDER_MODE_MONO ==> 1 bit value ==> faster generation ...
if (0!=error) {
EWOL_ERROR("FT_Render_Glyph");
return false;
}
// resize output image :
_imageOut.resize(ivec2(slot->bitmap.width, slot->bitmap.rows), 0);
for(int32_t jjj=0; jjj < slot->bitmap.rows;jjj++) {
for(int32_t iii=0; iii < slot->bitmap.width; iii++){
uint8_t valueColor = slot->bitmap.buffer[iii + slot->bitmap.width*jjj];
// real set of color
_imageOut.set(ivec2(iii, jjj), valueColor );
}
}
return true;
}
void ewol::resource::FontFreeType::generateKerning(int32_t fontSize, std::vector<ewol::GlyphProperty>& listGlyph) {
if(false == m_init) {

View File

@ -11,6 +11,7 @@
#include <etk/types.h>
#include <ewol/resource/font/FontBase.h>
#include <egami/egami.h>
extern "C" {
#include <freetype/ft2build.h>
@ -41,6 +42,10 @@ namespace ewol {
ewol::GlyphProperty& _property,
int8_t _posInImage);
bool drawGlyph(egami::ImageMono& _imageOut,
int32_t _fontSize,
ewol::GlyphProperty& _property);
vec2 getSize(int32_t _fontSize, const std::string& _unicodeString);
int32_t getHeight(int32_t _fontSize);

View File

@ -44,7 +44,7 @@ etk::CCout& ewol::operator <<(etk::CCout& _os, enum ewol::font::mode _obj) {
ewol::resource::TexturedFont::TexturedFont(const std::string& _fontName) :
ewol::resource::Texture(_fontName) {
addObjectType("ewol::compositing::TexturedFont");
addObjectType("ewol::resource::TexturedFont");
m_font[0] = NULL;
m_font[1] = NULL;
m_font[2] = NULL;

View File

@ -12,6 +12,7 @@
#include <etk/types.h>
#include <ewol/debug.h>
#include <egami/Image.h>
#include <egami/ImageMono.h>
#include <ewol/resource/Texture.h>
#include <ewol/resource/Resource.h>
#include <ewol/resource/font/GlyphProperty.h>
@ -34,6 +35,10 @@ namespace ewol {
ewol::GlyphProperty& _property,
int8_t _posInImage) = 0;
virtual bool drawGlyph(egami::ImageMono& _imageOut,
int32_t _fontSize,
ewol::GlyphProperty& _property) = 0;
virtual vec2 getSize(int32_t _fontSize, const std::string& _unicodeString) = 0;
virtual int32_t getHeight(int32_t _fontSize) = 0;

View File

@ -103,6 +103,7 @@ def create(target):
'ewol/resource/Shader.cpp',
'ewol/resource/Texture.cpp',
'ewol/resource/TexturedFont.cpp',
'ewol/resource/DistanceFieldFont.cpp',
'ewol/resource/VirtualBufferObject.cpp'
])