From 9c6fe980180703851babaf4ca4f1c27b7416526a Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Tue, 7 Jan 2014 21:33:46 +0100 Subject: [PATCH] [DEV] Test distance field developpement --- external/egami | 2 +- sources/ewol/resource/DistanceFieldFont.cpp | 357 ++++++++++++++++++++ sources/ewol/resource/DistanceFieldFont.h | 95 ++++++ sources/ewol/resource/FontFreeType.cpp | 44 +++ sources/ewol/resource/FontFreeType.h | 5 + sources/ewol/resource/TexturedFont.cpp | 2 +- sources/ewol/resource/font/FontBase.h | 5 + sources/lutin_ewol.py | 1 + 8 files changed, 509 insertions(+), 2 deletions(-) create mode 100644 sources/ewol/resource/DistanceFieldFont.cpp create mode 100644 sources/ewol/resource/DistanceFieldFont.h diff --git a/external/egami b/external/egami index 20ac6565..cd88edc7 160000 --- a/external/egami +++ b/external/egami @@ -1 +1 @@ -Subproject commit 20ac6565ca545f15249fc9d492e2aed175624fad +Subproject commit cd88edc7085edbef3d78bee02104c697a0713057 diff --git a/sources/ewol/resource/DistanceFieldFont.cpp b/sources/ewol/resource/DistanceFieldFont.cpp new file mode 100644 index 00000000..5ea7431a --- /dev/null +++ b/sources/ewol/resource/DistanceFieldFont.cpp @@ -0,0 +1,357 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2011, Edouard DUPIN, all right reserved + * + * @license BSD v3 (see license file) + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#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 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 output; + myFolder.folderGetRecursiveFiles(output); + std::vector 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 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 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()0 && _pos.y()0 && _pos.x()0 && _pos.y()= 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(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(_object); + if (getManager().release(object2) == true) { + EWOL_DEBUG("REMOVE: DistanceFieldFont : file : '" << name << "' count=" << count); + //etk::displayBacktrace(false); + } + _object = NULL; +} diff --git a/sources/ewol/resource/DistanceFieldFont.h b/sources/ewol/resource/DistanceFieldFont.h new file mode 100644 index 00000000..3bcba48a --- /dev/null +++ b/sources/ewol/resource/DistanceFieldFont.h @@ -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 +#include +#include +#include + +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 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 + diff --git a/sources/ewol/resource/FontFreeType.cpp b/sources/ewol/resource/FontFreeType.cpp index 1bb4952b..0cc7c7a4 100644 --- a/sources/ewol/resource/FontFreeType.cpp +++ b/sources/ewol/resource/FontFreeType.cpp @@ -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& listGlyph) { if(false == m_init) { diff --git a/sources/ewol/resource/FontFreeType.h b/sources/ewol/resource/FontFreeType.h index a2b4d3b3..bb70f805 100644 --- a/sources/ewol/resource/FontFreeType.h +++ b/sources/ewol/resource/FontFreeType.h @@ -11,6 +11,7 @@ #include #include +#include extern "C" { #include @@ -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); diff --git a/sources/ewol/resource/TexturedFont.cpp b/sources/ewol/resource/TexturedFont.cpp index c4230f2b..d1adce71 100644 --- a/sources/ewol/resource/TexturedFont.cpp +++ b/sources/ewol/resource/TexturedFont.cpp @@ -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; diff --git a/sources/ewol/resource/font/FontBase.h b/sources/ewol/resource/font/FontBase.h index 33ce74ac..b1673354 100644 --- a/sources/ewol/resource/font/FontBase.h +++ b/sources/ewol/resource/font/FontBase.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/sources/lutin_ewol.py b/sources/lutin_ewol.py index 7c1e0942..71b69558 100755 --- a/sources/lutin_ewol.py +++ b/sources/lutin_ewol.py @@ -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' ])