From 39947908793c024169708d70fe9f460c4c2efec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 Jun 2013 17:21:15 +0200 Subject: [PATCH] Trade: reworked AbstractImageConverter plugin interface. Implementation is moved into private virtual `do*()` functions and public interface does additional sanity checks around them. Exporting to file is by default done in base implementation, which takes exported data and then saves them to the file. Added unit test for file export, bumped plugin interface version to 0.2. --- src/Trade/AbstractImageConverter.cpp | 48 +++++++++--- src/Trade/AbstractImageConverter.h | 72 ++++++++++++------ src/Trade/Test/AbstractImageConverterTest.cpp | 75 +++++++++++++++++++ src/Trade/Test/CMakeLists.txt | 1 + src/Trade/Test/testConfigure.h.cmake | 1 + 5 files changed, 162 insertions(+), 35 deletions(-) create mode 100644 src/Trade/Test/AbstractImageConverterTest.cpp diff --git a/src/Trade/AbstractImageConverter.cpp b/src/Trade/AbstractImageConverter.cpp index 2a06d5b63..d0a465f68 100644 --- a/src/Trade/AbstractImageConverter.cpp +++ b/src/Trade/AbstractImageConverter.cpp @@ -24,6 +24,7 @@ #include "AbstractImageConverter.h" +#include #include #include @@ -33,25 +34,48 @@ AbstractImageConverter::AbstractImageConverter() = default; AbstractImageConverter::AbstractImageConverter(PluginManager::AbstractManager* manager, std::string plugin): AbstractPlugin(manager, std::move(plugin)) {} -Image2D* AbstractImageConverter::convertToImage(const Image2D* const) const { - CORRADE_ASSERT(features() & Feature::ConvertToImage, - "Trade::AbstractImageConverter::convertToImage(): feature advertised but not implemented", nullptr); +Image2D* AbstractImageConverter::exportToImage(const Image2D* const image) const { + CORRADE_ASSERT(features() & Feature::ConvertImage, + "Trade::AbstractImageConverter::exportToImage(): feature not supported", nullptr); - CORRADE_ASSERT(false, "Trade::AbstractImageConverter::convertToImage(): feature not implemented", nullptr); + return doExportToImage(image); } -Containers::Array AbstractImageConverter::convertToData(const Image2D* const) const { - CORRADE_ASSERT(features() & Feature::ConvertToData, - "Trade::AbstractImageConverter::convertToData(): feature advertised but not implemented", {}); +Image2D* AbstractImageConverter::doExportToImage(const Image2D*) const { + CORRADE_ASSERT(false, "Trade::AbstractImageConverter::exportToImage(): feature advertised but not implemented", nullptr); +} + +Containers::Array AbstractImageConverter::exportToData(const Image2D* const image) const { + CORRADE_ASSERT(features() & Feature::ConvertData, + "Trade::AbstractImageConverter::exportToData(): feature not supported", nullptr); + + return doExportToData(image); +} - CORRADE_ASSERT(false, "Trade::AbstractImageConverter::convertToData(): feature not implemented", {}); +Containers::Array AbstractImageConverter::doExportToData(const Image2D*) const { + CORRADE_ASSERT(false, "Trade::AbstractImageConverter::exportToData(): feature advertised but not implemented", nullptr); } -bool AbstractImageConverter::convertToFile(const Image2D* const, const std::string&) const { - CORRADE_ASSERT(features() & Feature::ConvertToFile, - "Trade::AbstractImageConverter::convertToFile(): feature advertised but not implemented", false); +bool AbstractImageConverter::exportToFile(const Image2D* const image, const std::string& filename) const { + return doExportToFile(image, filename); +} + +bool AbstractImageConverter::doExportToFile(const Image2D* const image, const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ConvertData, "Trade::AbstractImageConverter::exportToFile(): not implemented", nullptr); + + auto data = doExportToData(image); + if(!data) return false; + + /* Open file */ + std::ofstream out(filename.data(), std::ios::binary); + if(!out.good()) { + Error() << "Trade::AbstractImageConverter::exportToFile(): cannot write to file" << filename; + return false; + } - CORRADE_ASSERT(false, "Trade::AbstractImageConverter::convertToFile(): feature not implemented", false); + /* Write data, close */ + out.write(reinterpret_cast(data.begin()), data.size()); + return true; } }} diff --git a/src/Trade/AbstractImageConverter.h b/src/Trade/AbstractImageConverter.h index 8cc609eff..126964286 100644 --- a/src/Trade/AbstractImageConverter.h +++ b/src/Trade/AbstractImageConverter.h @@ -44,12 +44,19 @@ or compressing them. @section AbstractImageConverter-subclassing Subclassing -Plugin implements function features() and one or more of convertToImage(), -convertToData() or convertToFile() functions based on what features are +Plugin implements function doFeatures() and one or more of doExportToImage(), +doExportToData() or doExportToFile() functions based on what features are supported. + +You don't need to do most of the redundant sanity checks, these things are +checked by the implementation: + +- Functions doExportToImage() or doExportToData() are called only if + @ref Feature "Feature::ConvertImage" or @ref Feature "Feature::ConvertData" + is supported. */ class MAGNUM_EXPORT AbstractImageConverter: public PluginManager::AbstractPlugin { - CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Trade.AbstractImageConverter/0.1") + CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Trade.AbstractImageConverter/0.2") public: /** @@ -58,14 +65,11 @@ class MAGNUM_EXPORT AbstractImageConverter: public PluginManager::AbstractPlugin * @see Features, features() */ enum class Feature: UnsignedByte { - /** Converting to image with different format with convertToImage() */ - ConvertToImage = 1 << 0, - - /** Converting to data with convertToData() */ - ConvertToData = 1 << 1, + /** Conversion to image with different format with exportToImage() */ + ConvertImage = 1 << 0, - /** Converting to file with convertToFile() */ - ConvertToFile = 1 << 2 + /** Exporting to raw data with exportToData() */ + ConvertData = 1 << 1 }; /** @@ -82,34 +86,56 @@ class MAGNUM_EXPORT AbstractImageConverter: public PluginManager::AbstractPlugin explicit AbstractImageConverter(PluginManager::AbstractManager* manager, std::string plugin); /** @brief Features supported by this converter */ - virtual Features features() const = 0; + Features features() const { return doFeatures(); } /** * @brief Convert image to different format * - * Available only if @ref Feature "Feature::ConvertToImage" is supported. + * Available only if @ref Feature "Feature::ConvertImage" is supported. * Returns converted image on success, `nullptr` otherwise. - * @see features(), convertToData(), convertToFile() + * @see features(), exportToData(), exportToFile() */ - virtual Image2D* convertToImage(const Image2D* image) const; + Image2D* exportToImage(const Image2D* image) const; /** - * @brief Convert image to raw data + * @brief Export image to raw data * - * Available only if @ref Feature "Feature::ConvertToData" is supported. - * Returns data pointer and size on success, `nullptr` otherwise. - * @see features(), convertToImage(), convertToFile() + * Available only if @ref Feature "Feature::ConvertData" is supported. + * Returns data on success, zero-sized array otherwise. + * @see features(), exportToImage(), exportToFile() */ - virtual Containers::Array convertToData(const Image2D* image) const; + Containers::Array exportToData(const Image2D* image) const; /** - * @brief Convert image and save it to file + * @brief Export image to file * - * Available only if @ref Feature "Feature::ConvertToFile" is supported. * Returns `true` on success, `false` otherwise. - * @see features(), convertToImage(), convertToData() + * @see features(), exportToImage(), exportToData() + */ + bool exportToFile(const Image2D* image, const std::string& filename) const; + + #ifndef DOXYGEN_GENERATING_OUTPUT + private: + #else + protected: + #endif + /** @brief Implementation of features() */ + virtual Features doFeatures() const = 0; + + /** @brief Implementation of exportToImage() */ + virtual Image2D* doExportToImage(const Image2D* image) const; + + /** @brief Implementation of exportToData() */ + virtual Containers::Array doExportToData(const Image2D* image) const; + + /** + * @brief Implementation of exportToFile() + * + * If @ref Feature "Feature::ConvertData" is supported, default + * implementation calls doExportToData() and saves the result to given + * file. */ - virtual bool convertToFile(const Image2D* image, const std::string& filename) const; + virtual bool doExportToFile(const Image2D* image, const std::string& filename) const; }; CORRADE_ENUMSET_OPERATORS(AbstractImageConverter::Features) diff --git a/src/Trade/Test/AbstractImageConverterTest.cpp b/src/Trade/Test/AbstractImageConverterTest.cpp new file mode 100644 index 000000000..3d209ea79 --- /dev/null +++ b/src/Trade/Test/AbstractImageConverterTest.cpp @@ -0,0 +1,75 @@ +/* + This file is part of Magnum. + + Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +#include "Image.h" +#include "ImageFormat.h" +#include "Trade/AbstractImageConverter.h" + +#include "testConfigure.h" + +namespace Magnum { namespace Trade { namespace Test { + +class AbstractImageConverterTest: public TestSuite::Tester { + public: + explicit AbstractImageConverterTest(); + + void exportToFile(); +}; + +AbstractImageConverterTest::AbstractImageConverterTest() { + addTests({&AbstractImageConverterTest::exportToFile}); +} + +void AbstractImageConverterTest::exportToFile() { + class DataExporter: public Trade::AbstractImageConverter { + private: + Features doFeatures() const override { return Feature::ConvertData; } + + Containers::Array doExportToData(const Image2D* image) const override { + Containers::Array out(2); + out[0] = image->size().x(); + out[1] = image->size().y(); + return out; + }; + }; + + /* Remove previous file */ + Utility::Directory::rm(Utility::Directory::join(TRADE_TEST_OUTPUT_DIR, "image.out")); + + /* doExportToFile() should call doExportToData() */ + DataExporter exporter; + Image2D image({0xfe, 0xed}, ImageFormat::RGBA, ImageType::UnsignedByte, nullptr); + CORRADE_VERIFY(exporter.exportToFile(&image, Utility::Directory::join(TRADE_TEST_OUTPUT_DIR, "image.out"))); + CORRADE_COMPARE_AS(Utility::Directory::join(TRADE_TEST_OUTPUT_DIR, "image.out"), + "\xFE\xED", TestSuite::Compare::FileToString); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::AbstractImageConverterTest) diff --git a/src/Trade/Test/CMakeLists.txt b/src/Trade/Test/CMakeLists.txt index 7d9c878e0..a3c163349 100644 --- a/src/Trade/Test/CMakeLists.txt +++ b/src/Trade/Test/CMakeLists.txt @@ -27,6 +27,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testConfigure.h.cmake include_directories(${CMAKE_CURRENT_BINARY_DIR}) +corrade_add_test(TradeAbstractImageConverterTest AbstractImageConverterTest.cpp LIBRARIES Magnum) corrade_add_test(TradeAbstractImporterTest AbstractImporterTest.cpp LIBRARIES Magnum) corrade_add_test(TradeObjectData2DTest ObjectData2DTest.cpp LIBRARIES Magnum) corrade_add_test(TradeObjectData3DTest ObjectData3DTest.cpp LIBRARIES Magnum) diff --git a/src/Trade/Test/testConfigure.h.cmake b/src/Trade/Test/testConfigure.h.cmake index 921a711c0..e2882497a 100644 --- a/src/Trade/Test/testConfigure.h.cmake +++ b/src/Trade/Test/testConfigure.h.cmake @@ -23,3 +23,4 @@ */ #define TRADE_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}" +#define TRADE_TEST_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}"