From 99eae9d10f25f267d567ffcfb70ec7e95155b1ee Mon Sep 17 00:00:00 2001 From: Edouard DUPIN Date: Mon, 9 Oct 2017 22:22:48 +0200 Subject: [PATCH] [DEV] add store in png --- egami/Image.cpp | 4 +- egami/Image.hpp | 4 +- egami/ImageMono.hpp | 4 +- egami/ImagePrivate.hpp | 4 +- egami/egami.cpp | 11 ++- egami/egami.hpp | 3 +- egami/wrapperPNG.cpp | 193 +++++++++++++++++++++++++++++++++-------- egami/wrapperPNG.hpp | 7 ++ sample/read.cpp | 2 +- sample/write.cpp | 28 +++++- 10 files changed, 208 insertions(+), 52 deletions(-) diff --git a/egami/Image.cpp b/egami/Image.cpp index ceb42ee..74eae79 100644 --- a/egami/Image.cpp +++ b/egami/Image.cpp @@ -156,12 +156,12 @@ enum egami::colorType egami::Image::getType() const { return m_data->getType(); } -void* egami::Image::getTextureDataPointer() { +void* egami::Image::getTextureDataPointer() const{ if (m_data == nullptr) { EGAMI_DEBUG("No internal data for image (nullptr)"); return nullptr; } - return m_data->getTextureDataPointer(); + return (void*)m_data->getTextureDataPointer(); } void egami::Image::resize(const ivec2& _size, const ivec2& _startPos) { diff --git a/egami/Image.hpp b/egami/Image.hpp index 94337ea..86b4f5b 100644 --- a/egami/Image.hpp +++ b/egami/Image.hpp @@ -37,7 +37,7 @@ namespace egami { public: ImagePrivate() {}; virtual ~ImagePrivate() {}; - virtual void* getTextureDataPointer() { + virtual void* getTextureDataPointer() const { return nullptr; }; virtual const ivec2& getSize() const = 0; @@ -89,7 +89,7 @@ namespace egami { public: void configure(const ivec2& _size=ivec2(32,32), enum colorType _type=egami::colorType::RGBA8); - void* getTextureDataPointer(); + void* getTextureDataPointer() const; enum colorType getType() const; bool exist() { return m_data != nullptr; diff --git a/egami/ImageMono.hpp b/egami/ImageMono.hpp index 3a523af..1da1d79 100644 --- a/egami/ImageMono.hpp +++ b/egami/ImageMono.hpp @@ -23,8 +23,8 @@ namespace egami { ~ImageMono() { }; // EWOL internal API for Texture system : public: - void* getTextureDataPointer() { - return &m_data[0]; + void* getTextureDataPointer() const { + return (void*)&m_data[0]; }; /* enum colorType getType() { diff --git a/egami/ImagePrivate.hpp b/egami/ImagePrivate.hpp index 1b2e007..4a6a0ba 100644 --- a/egami/ImagePrivate.hpp +++ b/egami/ImagePrivate.hpp @@ -37,8 +37,8 @@ namespace egami { virtual ~ImageTemplate() { }; // EWOL internal API for Texture system: public: - void* getTextureDataPointer() { - return &m_data[0]; + void* getTextureDataPointer() const { + return (void*)&m_data[0]; }; enum colorType getType() const; const ivec2& getSize() { diff --git a/egami/egami.cpp b/egami/egami.cpp index c5f4e37..a65d2c0 100644 --- a/egami/egami.cpp +++ b/egami/egami.cpp @@ -151,8 +151,10 @@ bool egami::store(const egami::Image& _input, const etk::String& _fileName) { EGAMI_ERROR("Can not store in SVG file '" << _fileName << "'"); return false; } else if (etk::end_with(tmpName, ".png") == true) { - EGAMI_ERROR("Can not store in PNG file '" << _fileName << "'"); - return false; + if (egami::storePNG(_fileName, _input) == false) { + EGAMI_ERROR("Error to store PNG file '" << _fileName << "'"); + return false; + } } else if (etk::end_with(tmpName, ".jpg") == true) { EGAMI_ERROR("Can not store in JPEG file '" << _fileName << "'"); return false; @@ -168,7 +170,10 @@ bool egami::store(const egami::Image& _input, const etk::String& _fileName) { } return true; } - +bool egami::store(const egami::Image& _input, etk::Vector& _buffer, const etk::String& _mineType) { + + return false; +} static void generateDistanceField(const egami::ImageMono& _input, egami::Image& _output) { diff --git a/egami/egami.hpp b/egami/egami.hpp index 54fe0c9..73db735 100644 --- a/egami/egami.hpp +++ b/egami/egami.hpp @@ -37,9 +37,10 @@ namespace egami { * @brief Save an image in a memory buffer. * @param[in] _input Data of the image. * @param[out] _buffer Store file in this buffer. + * @param[in] _mineType mineType of the output buffer. * @return true if the file is corectly Stored, false otherwise */ - bool store(const egami::Image& _input, etk::Vector& _buffer); + bool store(const egami::Image& _input, etk::Vector& _buffer, const etk::String& _mineType); /** * @brief know if a file can have multiple size definition. * @param[in] _fileName Name of the file. diff --git a/egami/wrapperPNG.cpp b/egami/wrapperPNG.cpp index 83b271b..9cbf5d3 100644 --- a/egami/wrapperPNG.cpp +++ b/egami/wrapperPNG.cpp @@ -14,7 +14,9 @@ namespace egami { class ReaderInstance { public: virtual ~ReaderInstance() = default; - virtual void read(png_bytep data, png_size_t length) = 0; + virtual void read(png_bytep _data, png_size_t _length) = 0; + virtual void write(png_bytep _data, png_size_t _length) = 0; + virtual void flush() = 0; }; class ReaderInstanceFSNode : public egami::ReaderInstance { @@ -25,57 +27,75 @@ namespace egami { m_data(_data) { } - void read(png_bytep data, png_size_t length) override { - m_data.fileRead(data, 1, length); + void read(png_bytep _data, png_size_t _length) override { + m_data.fileRead(_data, 1, _length); + } + void write(png_bytep _data, png_size_t _length) override { + m_data.fileWrite(_data, 1, _length); + } + void flush() override { + m_data.fileFlush(); } }; class ReaderInstanceBuffer : public egami::ReaderInstance { private: - const etk::Vector& m_data; + etk::Vector& m_data; int32_t m_offset; public: ReaderInstanceBuffer(const etk::Vector& _data, int32_t _offset): - m_data(_data), + m_data(const_cast&>(_data)), m_offset(_offset) { + } + ReaderInstanceBuffer(etk::Vector& _data): + m_data(_data), + m_offset(0) { + } void read(png_bytep data, png_size_t length) override { memcpy(data, &m_data[m_offset], length); m_offset += length; } + void write(png_bytep _data, png_size_t _length) override { + for (uint32_t iii=0; iii<_length; ++iii) { + m_data.pushBack(_data[iii]); + m_offset++; + } + } + void flush() override { + // nothing to do ... + } }; } // we must change the access of the IO of the png lib : -static void local_ReadData(png_structp png_ptr, png_bytep data, png_size_t length) { - egami::ReaderInstance* instance = static_cast(png_get_io_ptr(png_ptr)); +static void local_ReadData(png_structp _pngPtr, png_bytep _data, png_size_t _length) { + egami::ReaderInstance* instance = static_cast(png_get_io_ptr(_pngPtr)); if (instance != nullptr) { - instance->read(data, length); - } -} -/* -static void LocalWriteData(png_structp png_ptr, png_bytep data, png_size_t length) -{ - etk::FSNode* fileNode = static_cast(png_get_io_ptr(png_ptr)); - if (NULL!=fileNode) { - fileNode->FileWrite(data, 1, length); + instance->read(_data, _length); } } -static void localFlushData(png_structp png_ptr) -{ - etk::FSNode* fileNode = static_cast(png_get_io_ptr(png_ptr)); - if (NULL!=fileNode) { - fileNode->FileFlush(); +static void Local_WriteData(png_structp _pngPtr, png_bytep _data, png_size_t _length) { + egami::ReaderInstance* instance = static_cast(png_get_io_ptr(_pngPtr)); + if (instance != nullptr) { + instance->write(_data, _length); } } -*/ -void user_error_fn(png_structp _pngPtr, png_const_charp _errorMsg) { +static void local_FlushData(png_structp _pngPtr) { + egami::ReaderInstance* instance = static_cast(png_get_io_ptr(_pngPtr)); + if (instance != nullptr) { + instance->flush(); + } +} + + +void userErrorFunction(png_structp _pngPtr, png_const_charp _errorMsg) { EGAMI_ERROR("libpng error: '" << _errorMsg << "'"); } -void user_warning_fn(png_structp _pngPtr, png_const_charp _warningMsg) { +void userWarningFunction(png_structp _pngPtr, png_const_charp _warningMsg) { EGAMI_WARNING("libpng warning: '" << _warningMsg << "'"); } @@ -92,7 +112,7 @@ static egami::Image genericLoader(png_structp _pngPtr, png_infop _infoPtr) { int interlace_type = 0; png_get_IHDR(_pngPtr, _infoPtr, &width, &height, &bit_depth, &colorType, &interlace_type, nullptr, nullptr); // reallocate the image - EGAMI_VERBOSE("Load PNG image : (" << width << "," << height << ")" ); + EGAMI_ERROR("Load PNG image : (" << width << "," << height << ") bitDepth=" << bit_depth << " colorType=" << colorType); switch (colorType) { case PNG_COLOR_TYPE_RGBA: out.configure(ivec2(width,height), egami::colorType::RGBA8); @@ -271,7 +291,7 @@ egami::Image egami::loadPNG(const etk::String& _inputFile) { } // PNG read setup - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, user_error_fn, user_warning_fn); + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, userErrorFunction, userWarningFunction); if (png_ptr == nullptr) { EGAMI_ERROR("Can not Allocate PNG structure"); fileName.fileClose(); @@ -297,16 +317,10 @@ egami::Image egami::loadPNG(const etk::String& _inputFile) { ReaderInstance* tmpPoiter = &tmpNode; - // overwrite the read and write function : + // overwrite the read function: png_set_read_fn(png_ptr, tmpPoiter, &local_ReadData); - /* - png_set_write_fn(png_ptr, - &fileName, - &LocalWriteData, - &localFlushData); - */ out = genericLoader(png_ptr, info_ptr); fileName.fileClose(); return out; @@ -314,14 +328,13 @@ egami::Image egami::loadPNG(const etk::String& _inputFile) { egami::Image egami::loadPNG(const etk::Vector& _buffer) { egami::Image out; - unsigned char header[8]; if (png_sig_cmp(&_buffer[0], 0, 8)) { EGAMI_ERROR("Invalid start buffer:"); return out; } // PNG read setup - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, user_error_fn, user_warning_fn); + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, userErrorFunction, userWarningFunction); if (png_ptr == nullptr) { EGAMI_ERROR("Can not Allocate PNG structure"); return out; @@ -336,10 +349,118 @@ egami::Image egami::loadPNG(const etk::Vector& _buffer) { egami::ReaderInstance* tmpPoiter = &tmpNode; - // overwrite the read and write function : + // Overwrite the read function: png_set_read_fn(png_ptr, tmpPoiter, &local_ReadData); out = genericLoader(png_ptr, info_ptr); return out; } + +bool egami::storePNG(const etk::String& _fileName, const egami::Image& _inputImage) { + /* create file */ + /*FILE *fp = fopen(file_name, "wb"); + if (!fp) { + abort_("[write_png_file] File %s could not be opened for writing", file_name); + } + */ + etk::FSNode fileName(_fileName); + if(fileName.fileOpenWrite() == false) { + EGAMI_ERROR("Can not find the file name='" << fileName << "'"); + return false; + } + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, userErrorFunction, userWarningFunction); + if (png_ptr == nullptr) { + EGAMI_ERROR("Can not Allocate PNG structure"); + return false; + } + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == nullptr) { + EGAMI_ERROR("Can not Allocate PNG info structure"); + png_destroy_write_struct(&png_ptr, nullptr); + fileName.fileClose(); + return false; + } + if (setjmp(png_jmpbuf(png_ptr))) { + EGAMI_ERROR("Error during init_io"); + png_destroy_write_struct(&png_ptr, &info_ptr); + fileName.fileClose(); + return false; + } + ReaderInstanceFSNode tmpNode(fileName); + + ReaderInstance* tmpPoiter = &tmpNode; + + // overwrite the write functions: + png_set_write_fn(png_ptr, + tmpPoiter, + &Local_WriteData, + &local_FlushData); + /* + TODO: + out = genericWriter(png_ptr, info_ptr); + + fileName.fileClose(); + */ + + //png_init_io(png_ptr, fp); + /* write header */ + if (setjmp(png_jmpbuf(png_ptr))) { + EGAMI_ERROR("Error jump setting"); + png_destroy_write_struct(&png_ptr, &info_ptr); + fileName.fileClose(); + return false; + } + png_byte bitDepth = 8; + png_byte colorType = 0; + switch(_inputImage.getType()) { + case egami::colorType::RGBA8: + colorType = PNG_COLOR_TYPE_RGB_ALPHA; + //bitDepth = 4; + break; + case egami::colorType::RGB8: + colorType = PNG_COLOR_TYPE_RGB; + //bitDepth = 3; + break; + default: + EGAMI_ERROR("PNG can not export an image with other type than RGB and RGBA request:" << _inputImage.getType()); + png_destroy_write_struct(&png_ptr, &info_ptr); + fileName.fileClose(); + return false; + } + png_set_IHDR(png_ptr, + info_ptr, + _inputImage.getSize().x(), + _inputImage.getSize().y(), + bitDepth, + colorType, + PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + png_write_info(png_ptr, info_ptr); + /* write bytes */ + if (setjmp(png_jmpbuf(png_ptr))) { + EGAMI_ERROR("Error while writing byte"); + png_destroy_write_struct(&png_ptr, &info_ptr); + fileName.fileClose(); + return false; + } + etk::Vector rowPointers; + rowPointers.resize(_inputImage.getSize().y(), NULL); + uint8_t* imageData = (uint8_t*)_inputImage.getTextureDataPointer(); + for (size_t iii=0; iii& _buffer); + /** + * @breif Store a PNG 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 storePNG(const etk::String& _fileName, const egami::Image& _inputImage); } diff --git a/sample/read.cpp b/sample/read.cpp index 0795479..a2f9c41 100644 --- a/sample/read.cpp +++ b/sample/read.cpp @@ -41,7 +41,7 @@ static void readSVG() { static void readPNG() { //! [egami_sample_read_file_png] - egami::Image image = egami::load("DATA:read.png"); + egami::Image image = egami::load("DATA:read_128x128.png"); //! [egami_sample_read_file_png] TEST_INFO("image exist (PNG): " << image.exist()); } diff --git a/sample/write.cpp b/sample/write.cpp index 70dd9aa..058f04b 100644 --- a/sample/write.cpp +++ b/sample/write.cpp @@ -13,14 +13,14 @@ static void writeBMP() { //! [egami_sample_create_image] // create an empty Image (no type and no inside data) - egami::Image image(ivec2(25,25)); + egami::Image image(ivec2(25,25), egami::colorType::RGBA8); image.set(ivec2(5,5), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); image.set(ivec2(12,15), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); image.set(ivec2(4,9), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); // ... //! [egami_sample_create_image] //! [egami_sample_write_file_bmp] - bool ret = egami::store(image, "DATA:read.bmp"); + bool ret = egami::store(image, "out/egami_test_write.bmp"); //! [egami_sample_write_file_bmp] TEST_INFO("image write (BMP): " << ret); } @@ -30,7 +30,28 @@ static void writeSVG() { } static void writePNG() { - TEST_INFO("image write (PNG): Not Avaliiable"); + // create an empty Image (no type and no inside data) + egami::Image image(ivec2(25,25), egami::colorType::RGBA8); + image.set(ivec2(5,5), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); + image.set(ivec2(12,15), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); + image.set(ivec2(4,9), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); + // ... + //! [egami_sample_write_file_png] + bool ret = egami::store(image, "out/egami_test_write.png"); + //! [egami_sample_write_file_png] + TEST_INFO("image write (PNG): " << ret); +} +static void writeJPG() { + // create an empty Image (no type and no inside data) + egami::Image image(ivec2(25,25), egami::colorType::RGBA8); + image.set(ivec2(5,5), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); + image.set(ivec2(12,15), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); + image.set(ivec2(4,9), etk::Color<>(0x88, 0xFF, 0x00, 0xFF)); + // ... + //! [egami_sample_write_file_jpg] + bool ret = egami::store(image, "out/egami_test_write.jpg"); + //! [egami_sample_write_file_jpg] + TEST_INFO("image write (JPG): " << ret); } @@ -38,6 +59,7 @@ void appl::write() { writeBMP(); writeSVG(); writePNG(); + writeJPG(); } //! [egami_sample_write_all]