diff --git a/egami/Image.cpp b/egami/Image.cpp index 415648a..fa91c17 100644 --- a/egami/Image.cpp +++ b/egami/Image.cpp @@ -7,6 +7,7 @@ */ #include +#include egami::Image::Image(const ivec2& _size) : m_size(_size) { @@ -43,6 +44,21 @@ void egami::Image::resize(const ivec2& _size, const ivec2& _startPos) { } } +void egami::Image::scale(const ivec2& _size) { + // TODO : Add capabilities ... + int32_t stepX = m_size.x() / _size.x(); + int32_t stepY = m_size.y() / _size.y(); + stepX = etk_max(1, stepX); + stepY = etk_max(1, stepY); + EGAMI_VERBOSE("move : " << stepX << " , " << stepY << " from : " << m_size << " ==> " << _size); + for (int32_t yyy = 0; yyy < _size.y(); ++yyy) { + for (int32_t xxx = 0; xxx < _size.x(); ++xxx) { + set(ivec2(xxx, yyy), get(ivec2(xxx*stepX, yyy*stepY))); + } + } + resize(_size); +} + void egami::Image::clear(etk::Color<> _fill) { for (int32_t iii=0; iii& get(const ivec2& _pos) const; void set(const ivec2& _pos, const etk::Color<>& _newColor); void insert(const ivec2& _pos, const egami::Image& _input); + /** + * @brief Scale an image in an other dimention. + * @param[in] _size Destination size of the image. + * @TODO Set this function more capacity like not a multiple ratio... + */ + void scale(const ivec2& _size); }; }; diff --git a/egami/egami.cpp b/egami/egami.cpp index 766373e..1c5979d 100644 --- a/egami/egami.cpp +++ b/egami/egami.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include bool egami::scalable(const std::string& _fileName) { if (true == end_with(_fileName, ".svg") ) { @@ -23,7 +25,12 @@ bool egami::scalable(const std::string& _fileName) { bool egami::load(egami::Image& _output, const std::string& _fileName, const ivec2& _size) { std::string tmpName = std::tolower(_fileName); // select the corect Loader : - if (true == end_with(tmpName, ".bmp") ) { + if (true == end_with(tmpName, ".edf") ) { // internal format for ewol distance field ==> simple sistance field image + if (false == egami::loadEDF(_fileName, _output)) { + EGAMI_ERROR("Error to load EDF file '" << _fileName << "'"); + return false; + } + } else if (true == end_with(tmpName, ".bmp") ) { if (false == egami::loadBMP(_fileName, _output)) { EGAMI_ERROR("Error to load BMP file '" << _fileName << "'"); return false; @@ -33,13 +40,14 @@ bool egami::load(egami::Image& _output, const std::string& _fileName, const ivec EGAMI_ERROR("Error to load SVG file '" << _fileName << "'"); return false; } + egami::storeEDF(_fileName + ".edf", _output); } else if (true == end_with(tmpName, ".png") ) { if (false == egami::loadPNG(_fileName, _output)) { EGAMI_ERROR("Error to load PNG file '" << _fileName << "'"); return false; } } else { - EGAMI_ERROR("Extention not managed '" << _fileName << "' Sopported extention : .bmp / .svg / .png"); + EGAMI_ERROR("Extention not managed '" << _fileName << "' Sopported extention : .edf / .bmp / .svg / .png"); return false; } return true; @@ -47,8 +55,14 @@ bool egami::load(egami::Image& _output, const std::string& _fileName, const ivec bool egami::store(const egami::Image& _input, const std::string& _fileName) { std::string tmpName = std::tolower(_fileName); + EGAMI_DEBUG("Store file : " << _fileName); // select the corect Loader : - if (true == end_with(tmpName, ".bmp") ) { + if (true == end_with(tmpName, ".edf") ) { + if (false == egami::storeEDF(_fileName, _input)) { + EGAMI_ERROR("Error to load EDF file '" << _fileName << "'"); + return false; + } + } else if (true == end_with(tmpName, ".bmp") ) { if (false == egami::storeBMP(_fileName, _input)) { EGAMI_ERROR("Error to load BMP file '" << _fileName << "'"); return false; @@ -65,3 +79,110 @@ bool egami::store(const egami::Image& _input, const std::string& _fileName) { } return true; } + + + +static void generateDistanceField(const egami::ImageMono& _input, egami::Image& _output) { + int32_t size = _input.getSize().x() * _input.getSize().y(); + std::vector xdist(size); + std::vector ydist(size); + std::vector gx(size); + std::vector gy(size); + std::vector data(size); + std::vector outside(size); + std::vector inside(size); + // Convert img into double (data) + double img_min = 255, img_max = -255; + for (int32_t yyy = 0; yyy < _input.getSize().y(); ++yyy) { + for (int32_t xxx = 0; xxx < _input.getSize().x(); ++xxx) { + int32_t iii = yyy * _input.getSize().x() + xxx; + double v = _input.get(ivec2(xxx, yyy)); + data[iii] = v; + if (v > img_max) { + img_max = v; + } + if (v < img_min) { + img_min = v; + } + } + } + // Rescale image levels between 0 and 1 + for (int32_t yyy = 0; yyy < _input.getSize().y(); ++yyy) { + for (int32_t xxx = 0; xxx < _input.getSize().x(); ++xxx) { + int32_t iii = yyy * _input.getSize().x() + xxx; + data[iii] = (_input.get(ivec2(xxx, yyy))-img_min)/img_max; + } + } + + // Compute outside = edtaa3(bitmap); % Transform background (0's) + computegradient(&data[0], _input.getSize().x(), _input.getSize().y(), &gx[0], &gy[0]); + edtaa3(&data[0], &gx[0], &gy[0], _input.getSize().x(), _input.getSize().y(), &xdist[0], &ydist[0], &outside[0]); + for(size_t iii = 0; iii < outside.size(); ++iii) { + if( outside[iii] < 0 ) { + outside[iii] = 0.0; + } + } + + // Compute inside = edtaa3(1-bitmap); % Transform foreground (1's) + for(size_t iii = 0; iii < gx.size(); ++iii) { + gx[iii] = 0; + } + for(size_t iii = 0; iii < gy.size(); ++iii) { + gy[iii] = 0; + } + for(size_t iii = 0; iii < data.size(); ++iii) { + data[iii] = 1 - data[iii]; + } + computegradient( &data[0], _input.getSize().x(), _input.getSize().y(), &gx[0], &gy[0]); + edtaa3(&data[0], &gx[0], &gy[0], _input.getSize().x(), _input.getSize().y(), &xdist[0], &ydist[0], &inside[0]); + for(size_t iii = 0; iii < inside.size(); ++iii) { + if( inside[iii] < 0 ) { + inside[iii] = 0.0; + } + } + + _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) { + int32_t iii = yyy * _output.getSize().x() + xxx; + outside[iii] -= inside[iii]; + outside[iii] = 128+outside[iii]*16; + if( outside[iii] < 0 ) { + outside[iii] = 0; + } + if( outside[iii] > 255 ) { + outside[iii] = 255; + } + uint8_t val = 255 - (unsigned char) outside[iii]; + // TODO : Remove multiple size of the map ... + _output.set(ivec2(xxx, yyy), etk::Color<>((int32_t)val,(int32_t)val,(int32_t)val,(int32_t)val)); + } + } +} + + +bool egami::generateDistanceFieldFile(const std::string& _input, const std::string& _output) { + egami::Image data; + if (std::end_with(_input, ".edf") == true) { + return false; + } + EGAMI_ERROR("Generate distance field : '" << _input << "' ==> '" << _output << "'"); + if (egami::load(data, _input, ivec2(64*5,64*5)) == false) { + return false; + } + // Generate distance field : + egami::ImageMono input; + input.resize(data.getSize()); + for (size_t yyy = 0; yyy < data.getSize().y(); ++yyy) { + for (size_t xxx = 0; xxx < data.getSize().x(); ++xxx) { + input.set(ivec2(xxx, yyy), data.get(ivec2(xxx, yyy)).a() ); + } + } + generateDistanceField(input, data); + // scale: + data.scale(ivec2(64,64)); + // store: + return egami::store(data, _output); +} + diff --git a/egami/egami.h b/egami/egami.h index ef65dff..fbd1a84 100644 --- a/egami/egami.h +++ b/egami/egami.h @@ -39,6 +39,14 @@ namespace egami * @return true if the format is scalable. */ bool scalable(const std::string& _fileName); + /** + * @brief Generate a distance field output file from an input file; + * @param[in] _input Input file name + * @param[in] _output Output file name + * @return true All done corectly. + * @return false An error occured. + */ + bool generateDistanceFieldFile(const std::string& _input, const std::string& _output); }; #endif diff --git a/egami/wrapperEDF.cpp b/egami/wrapperEDF.cpp new file mode 100644 index 0000000..08edf43 --- /dev/null +++ b/egami/wrapperEDF.cpp @@ -0,0 +1,125 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2011, Edouard DUPIN, all right reserved + * + * @license BSD v3 (see license file) + */ + +#include +#include +#include +#include +#include + +#undef __class__ +#define __class__ "wrapperEDF" + +//EDF format is a simple format for image in text for distance field image (special case) +// it is composed of the fist line : description of type (starting with #EDF and some other information, the data start just after the first \n + +bool egami::loadEDF(const std::string& _inputFile, egami::Image& _ouputImage) { + + etk::FSNode file(_inputFile); + if (false == file.exist()) { + EGAMI_ERROR("File does not existed='" << file << "'"); + return false; + } + if(false == file.fileOpenRead() ) { + EGAMI_ERROR("Can not find the file name='" << file << "'"); + return false; + } + std::string line; + file.fileGets(line); + if (std::start_with(line, "#edf", false) == false) { + EGAMI_ERROR("This file seams not to be a EDF file ..."); + file.fileClose(); + return false; + } + // count number of colomn max an number of line max: + ivec2 size(0,0); + while (file.fileGets(line) == true) { + if (line.size()/2 > (size_t)size.x()) { + size.setValue(line.size()/2, size.y()+1); + } else { + size += ivec2(0,1); + } + } + if (line.size()/2 > (size_t)size.x()) { + size.setValue(line.size()/2, size.y()+1); + } else { + size += ivec2(0,1); + } + EGAMI_DEBUG("'" << file << "' ==> size=" << size); + // jup to the start of the file + file.fileSeek(0, etk::FSN_SEEK_START); + // drop the first line + file.fileGets(line); + + + // resize output: + _ouputImage.resize(size); + int32_t currentLineId = 0; + char tmp[3]; + tmp[2] = '\0'; + while (file.fileGets(line) == true) { + if (line.size() <= 0) { + continue; + } + for (size_t xxx = 0; xxx < line.size()-1; xxx+=2) { + tmp[0] = line[xxx]; + tmp[1] = line[xxx+1]; + int32_t val = 0; + sscanf(tmp, "%x", &val); + _ouputImage.set(ivec2(xxx/2, currentLineId), etk::Color<>((uint8_t)val, (uint8_t)val, (uint8_t)val, (uint8_t)val)); + } + ++currentLineId; + } + if (line.size() > 0) { + for (size_t xxx = 0; xxx < line.size()-1; xxx+=2) { + tmp[0] = line[xxx]; + tmp[1] = line[xxx+1]; + int32_t val = 0; + sscanf(tmp, "%x", &val); + _ouputImage.set(ivec2(xxx/2, currentLineId), etk::Color<>((uint8_t)val, (uint8_t)val, (uint8_t)val, (uint8_t)val)); + } + } + file.fileClose(); + return true; +} + +bool egami::storeEDF(const std::string& _fileName, const egami::Image& _inputImage) { + bool anErrorEccured = false; + etk::FSNode file(_fileName); + if (file.fileOpenWrite() == false) { + EGAMI_ERROR("Can not find the file name=\"" << file << "\""); + return false; + } + anErrorEccured = file.filePuts( std::string("#EDF // Generate with EGAMI (") + + std::to_string(_inputImage.getSize().x()) + + "," + + std::to_string(_inputImage.getSize().y()) + ")\n"); + + char tmp[256]; + for (int32_t yyy = 0; yyy < _inputImage.getSize().y(); ++yyy) { + if (yyy != 0) { + if (file.filePut('\n') == false) { + anErrorEccured = false; + } + } + for (int32_t xxx = 0; xxx < _inputImage.getSize().x(); ++xxx) { + sprintf(tmp, "%02X", _inputImage.get(ivec2(xxx, yyy)).a()); + /* + if (yyy == 25) { + EGAMI_DEBUG(" set : " << _inputImage.get(ivec2(xxx, yyy)) << " : '" << tmp << "'"); + } + */ + if (file.filePuts(tmp) == false) { + anErrorEccured = false; + } + } + } + + file.fileClose(); + return anErrorEccured; +} diff --git a/egami/wrapperEDF.h b/egami/wrapperEDF.h new file mode 100644 index 0000000..f3b4d14 --- /dev/null +++ b/egami/wrapperEDF.h @@ -0,0 +1,41 @@ +/** + * @author Edouard DUPIN + * + * @copyright 2011, Edouard DUPIN, all right reserved + * + * @license BSD v3 (see license file) + */ + +#ifndef __EGAMI_WRAPPER_EDF_H__ +#define __EGAMI_WRAPPER_EDF_H__ + +#include + +namespace egami { + /** + * @breif Load a bmp file in the image. + * A simple example of EDF file is : + * [PRE] + * #EDF //Example Of EDF file (5,5) + * * + * * * + * * * + * * * + * * + * [PRE] + * @param[in] _fileName Name of the file. + * @param[out] _ouputImage Read data. + * @return true if all is done correctly, false otherwise. + */ + bool loadEDF(const std::string& _fileName, egami::Image& _ouputImage); + /** + * @breif Store a edf file in the image. + * @param[in] _fileName Name of the file. + * @param[in] _inputImage write data. + * @return true if all is done correctly, false otherwise. + */ + bool storeEDF(const std::string& _fileName, const egami::Image& _inputImage); +}; + +#endif + diff --git a/lutin_egami.py b/lutin_egami.py index b580df8..cd0820f 100644 --- a/lutin_egami.py +++ b/lutin_egami.py @@ -20,10 +20,11 @@ def create(target): 'egami/debug.cpp', 'egami/wrapperPNG.cpp', 'egami/wrapperSVG.cpp', - 'egami/wrapperBMP.cpp']) + 'egami/wrapperBMP.cpp', + 'egami/wrapperEDF.cpp']) # name of the dependency - myModule.add_module_depend(['etk', 'png', 'esvg']) + myModule.add_module_depend(['etk', 'png', 'esvg', 'edtaa3']) myModule.compile_flags_CC([ '-Wno-write-strings',