From d53110c3acad6652430c4a372f17d15718e94381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 14 Jun 2013 21:38:27 +0200 Subject: [PATCH 01/36] Trade: use Corrade::Containers::Array in AbstractImageConverter. --- src/Trade/AbstractImageConverter.cpp | 3 ++- src/Trade/AbstractImageConverter.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Trade/AbstractImageConverter.cpp b/src/Trade/AbstractImageConverter.cpp index ec548d374..2a06d5b63 100644 --- a/src/Trade/AbstractImageConverter.cpp +++ b/src/Trade/AbstractImageConverter.cpp @@ -24,6 +24,7 @@ #include "AbstractImageConverter.h" +#include #include namespace Magnum { namespace Trade { @@ -39,7 +40,7 @@ Image2D* AbstractImageConverter::convertToImage(const Image2D* const) const { CORRADE_ASSERT(false, "Trade::AbstractImageConverter::convertToImage(): feature not implemented", nullptr); } -std::pair AbstractImageConverter::convertToData(const Image2D* const) const { +Containers::Array AbstractImageConverter::convertToData(const Image2D* const) const { CORRADE_ASSERT(features() & Feature::ConvertToData, "Trade::AbstractImageConverter::convertToData(): feature advertised but not implemented", {}); diff --git a/src/Trade/AbstractImageConverter.h b/src/Trade/AbstractImageConverter.h index b2826b96c..8cc609eff 100644 --- a/src/Trade/AbstractImageConverter.h +++ b/src/Trade/AbstractImageConverter.h @@ -100,7 +100,7 @@ class MAGNUM_EXPORT AbstractImageConverter: public PluginManager::AbstractPlugin * Returns data pointer and size on success, `nullptr` otherwise. * @see features(), convertToImage(), convertToFile() */ - virtual std::pair convertToData(const Image2D* image) const; + virtual Containers::Array convertToData(const Image2D* image) const; /** * @brief Convert image and save it to file From 80fc4611de5411759eb48ee83769a28160c611a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 Jun 2013 17:08:10 +0200 Subject: [PATCH 02/36] Trade: reworked AbstractImporter plugin interface. Implementation is moved into private virtual `do*()` functions and the public interface does additional checks aroung them to simplify plugin development. Opening files is by default done by the base implementation, which then calls function for opening raw data with file contents. Added test for file opening, bumped plugin interface version to 0.3. Y# Please enter the commit message for your changes. Lines starting --- src/Trade/AbstractImporter.cpp | 461 +++++++++++++++++++++--- src/Trade/AbstractImporter.h | 324 +++++++++++++---- src/Trade/Test/AbstractImporterTest.cpp | 69 ++++ src/Trade/Test/CMakeLists.txt | 6 + src/Trade/Test/file.bin | 1 + src/Trade/Test/testConfigure.h.cmake | 25 ++ 6 files changed, 764 insertions(+), 122 deletions(-) create mode 100644 src/Trade/Test/AbstractImporterTest.cpp create mode 100644 src/Trade/Test/file.bin create mode 100644 src/Trade/Test/testConfigure.h.cmake diff --git a/src/Trade/AbstractImporter.cpp b/src/Trade/AbstractImporter.cpp index f0319dd51..f6c53eb4a 100644 --- a/src/Trade/AbstractImporter.cpp +++ b/src/Trade/AbstractImporter.cpp @@ -24,6 +24,8 @@ #include "AbstractImporter.h" +#include +#include #include namespace Magnum { namespace Trade { @@ -32,55 +34,416 @@ AbstractImporter::AbstractImporter() = default; AbstractImporter::AbstractImporter(PluginManager::AbstractManager* manager, std::string plugin): AbstractPlugin(manager, std::move(plugin)) {} -bool AbstractImporter::openData(const void* const, const std::size_t) { +bool AbstractImporter::openData(Containers::ArrayReference data) { CORRADE_ASSERT(features() & Feature::OpenData, - "Trade::AbstractImporter::openData(): feature advertised but not implemented", nullptr); - - CORRADE_ASSERT(false, "Trade::AbstractImporter::openData(): feature not implemented", nullptr); -} - -bool AbstractImporter::openFile(const std::string&) { - CORRADE_ASSERT(features() & Feature::OpenFile, - "Trade::AbstractImporter::openFile(): feature advertised but not implemented", nullptr); - - CORRADE_ASSERT(false, "Trade::AbstractImporter::openFile(): feature not implemented", nullptr); -} - -Int AbstractImporter::sceneForName(const std::string&) { return -1; } -std::string AbstractImporter::sceneName(UnsignedInt) { return {}; } -SceneData* AbstractImporter::scene(UnsignedInt) { return nullptr; } -Int AbstractImporter::lightForName(const std::string&) { return -1; } -std::string AbstractImporter::lightName(UnsignedInt) { return {}; } -LightData* AbstractImporter::light(UnsignedInt) { return nullptr; } -Int AbstractImporter::cameraForName(const std::string&) { return -1; } -std::string AbstractImporter::cameraName(UnsignedInt) { return {}; } -CameraData* AbstractImporter::camera(UnsignedInt) { return nullptr; } -Int AbstractImporter::object2DForName(const std::string&) { return -1; } -std::string AbstractImporter::object2DName(UnsignedInt) { return {}; } -ObjectData2D* AbstractImporter::object2D(UnsignedInt) { return nullptr; } -Int AbstractImporter::object3DForName(const std::string&) { return -1; } -std::string AbstractImporter::object3DName(UnsignedInt) { return {}; } -ObjectData3D* AbstractImporter::object3D(UnsignedInt) { return nullptr; } -Int AbstractImporter::mesh2DForName(const std::string&) { return -1; } -std::string AbstractImporter::mesh2DName(UnsignedInt) { return {}; } -MeshData2D* AbstractImporter::mesh2D(UnsignedInt) { return nullptr; } -Int AbstractImporter::mesh3DForName(const std::string&) { return -1; } -std::string AbstractImporter::mesh3DName(UnsignedInt) { return {}; } -MeshData3D* AbstractImporter::mesh3D(UnsignedInt) { return nullptr; } -Int AbstractImporter::materialForName(const std::string&) { return -1; } -std::string AbstractImporter::materialName(UnsignedInt) { return {}; } -AbstractMaterialData* AbstractImporter::material(UnsignedInt) { return nullptr; } -Int AbstractImporter::textureForName(const std::string&) { return -1; } -std::string AbstractImporter::textureName(UnsignedInt) { return {}; } -TextureData* AbstractImporter::texture(UnsignedInt) { return nullptr; } -Int AbstractImporter::image1DForName(const std::string&) { return -1; } -std::string AbstractImporter::image1DName(UnsignedInt) { return {}; } -ImageData1D* AbstractImporter::image1D(UnsignedInt) { return nullptr; } -Int AbstractImporter::image2DForName(const std::string&) { return -1; } -std::string AbstractImporter::image2DName(UnsignedInt) { return {}; } -ImageData2D* AbstractImporter::image2D(UnsignedInt) { return nullptr; } -Int AbstractImporter::image3DForName(const std::string&) { return -1; } -std::string AbstractImporter::image3DName(UnsignedInt) { return {}; } -ImageData3D* AbstractImporter::image3D(UnsignedInt) { return nullptr; } + "Trade::AbstractImporter::openData(): feature not supported", nullptr); + + close(); + doOpenData(data); + return isOpened(); +} + +void AbstractImporter::doOpenData(Containers::ArrayReference) { + CORRADE_ASSERT(false, "Trade::AbstractImporter::openData(): feature advertised but not implemented", ); +} + +bool AbstractImporter::openFile(const std::string& filename) { + close(); + doOpenFile(filename); + return isOpened(); +} + +void AbstractImporter::doOpenFile(const std::string& filename) { + CORRADE_ASSERT(features() & Feature::OpenData, "Trade::AbstractImporter::openFile(): not implemented", ); + + /* Open file */ + std::ifstream in(filename.data(), std::ios::binary); + if(!in.good()) { + Error() << "Trade::AbstractImporter::openFile(): cannot open file" << filename; + return; + } + + /* Create array to hold file contents */ + in.seekg(0, std::ios::end); + Containers::Array data(in.tellg()); + + /* Read data, close */ + in.seekg(0, std::ios::beg); + in.read(reinterpret_cast(data.begin()), data.size()); + in.close(); + + doOpenData(data); +} + +void AbstractImporter::close() { + if(isOpened()) doClose(); +} + +Int AbstractImporter::defaultScene() { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::defaultScene(): no file opened", -1); + return doDefaultScene(); +} + +Int AbstractImporter::doDefaultScene() { return -1; } + +UnsignedInt AbstractImporter::sceneCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::sceneCount(): no file opened", 0); + return doSceneCount(); +} + +UnsignedInt AbstractImporter::doSceneCount() const { return 0; } + +Int AbstractImporter::sceneForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::sceneForName(): no file opened", -1); + return doSceneForName(name); +} + +Int AbstractImporter::doSceneForName(const std::string&) { return -1; } + +std::string AbstractImporter::sceneName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::sceneName(): no file opened", {}); + CORRADE_ASSERT(id < doSceneCount(), "Trade::AbstractImporter::sceneName(): index out of range", {}); + return doSceneName(id); +} + +std::string AbstractImporter::doSceneName(UnsignedInt) { return {}; } + +SceneData* AbstractImporter::scene(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::scene(): no file opened", {}); + CORRADE_ASSERT(id < doSceneCount(), "Trade::AbstractImporter::scene(): index out of range", {}); + return doScene(id); +} + +SceneData* AbstractImporter::doScene(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::lightCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::lightCount(): no file opened", {}); + return doLightCount(); +} + +UnsignedInt AbstractImporter::doLightCount() const { return 0; } + +Int AbstractImporter::lightForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::lightForName(): no file opened", {}); + return doLightForName(name); +} + +Int AbstractImporter::doLightForName(const std::string&) { return -1; } + +std::string AbstractImporter::lightName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::lightName(): no file opened", {}); + CORRADE_ASSERT(id < doLightCount(), "Trade::AbstractImporter::lightName(): index out of range", {}); + return doLightName(id); +} + +std::string AbstractImporter::doLightName(UnsignedInt) { return {}; } + +LightData* AbstractImporter::light(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::light(): no file opened", {}); + CORRADE_ASSERT(id < doLightCount(), "Trade::AbstractImporter::light(): index out of range", {}); + return doLight(id); +} + +LightData* AbstractImporter::doLight(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::cameraCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::cameraCount(): no file opened", {}); + return doCameraCount(); +} + +UnsignedInt AbstractImporter::doCameraCount() const { return 0; } + +Int AbstractImporter::cameraForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::cameraForName(): no file opened", {}); + return doCameraForName(name); +} + +Int AbstractImporter::doCameraForName(const std::string&) { return -1; } + +std::string AbstractImporter::cameraName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::cameraName(): no file opened", {}); + CORRADE_ASSERT(id < doCameraCount(), "Trade::AbstractImporter::cameraName(): index out of range", {}); + return doCameraName(id); +} + +std::string AbstractImporter::doCameraName(UnsignedInt) { return {}; } + +CameraData* AbstractImporter::camera(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::camera(): no file opened", {}); + CORRADE_ASSERT(id < doCameraCount(), "Trade::AbstractImporter::camera(): index out of range", {}); + return doCamera(id); +} + +CameraData* AbstractImporter::doCamera(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::object2DCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2DCount(): no file opened", {}); + return doObject2DCount(); +} + +UnsignedInt AbstractImporter::doObject2DCount() const { return 0; } + +Int AbstractImporter::object2DForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2DForName(): no file opened", {}); + return doObject2DForName(name); +} + +Int AbstractImporter::doObject2DForName(const std::string&) { return -1; } + +std::string AbstractImporter::object2DName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2DName(): no file opened", {}); + CORRADE_ASSERT(id < doObject2DCount(), "Trade::AbstractImporter::object2DName(): index out of range", {}); + return doObject2DName(id); +} + +std::string AbstractImporter::doObject2DName(UnsignedInt) { return {}; } + +ObjectData2D* AbstractImporter::object2D(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object2D(): no file opened", {}); + CORRADE_ASSERT(id < doObject2DCount(), "Trade::AbstractImporter::object2D(): index out of range", {}); + return doObject2D(id); +} + +ObjectData2D* AbstractImporter::doObject2D(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::object3DCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3DCount(): no file opened", {}); + return doObject3DCount(); +} + +UnsignedInt AbstractImporter::doObject3DCount() const { return 0; } + +Int AbstractImporter::object3DForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3DForName(): no file opened", {}); + return doObject3DForName(name); +} + +Int AbstractImporter::doObject3DForName(const std::string&) { return -1; } + +std::string AbstractImporter::object3DName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3DName(): no file opened", {}); + CORRADE_ASSERT(id < doObject3DCount(), "Trade::AbstractImporter::object3DName(): index out of range", {}); + return doObject3DName(id); +} + +std::string AbstractImporter::doObject3DName(UnsignedInt) { return {}; } + +ObjectData3D* AbstractImporter::object3D(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::object3D(): no file opened", {}); + CORRADE_ASSERT(id < doObject3DCount(), "Trade::AbstractImporter::object3D(): index out of range", {}); + return doObject3D(id); +} + +ObjectData3D* AbstractImporter::doObject3D(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::mesh2DCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh2DCount(): no file opened", {}); + return doMesh2DCount(); +} + +UnsignedInt AbstractImporter::doMesh2DCount() const { return 0; } + +Int AbstractImporter::mesh2DForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh2DForName(): no file opened", {}); + return doMesh2DForName(name); +} + +Int AbstractImporter::doMesh2DForName(const std::string&) { return -1; } + +std::string AbstractImporter::mesh2DName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh2DName(): no file opened", {}); + CORRADE_ASSERT(id < doMesh2DCount(), "Trade::AbstractImporter::object2DName(): index out of range", {}); + return doMesh2DName(id); +} + +std::string AbstractImporter::doMesh2DName(UnsignedInt) { return {}; } + +MeshData2D* AbstractImporter::mesh2D(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh2D(): no file opened", {}); + CORRADE_ASSERT(id < doMesh2DCount(), "Trade::AbstractImporter::object2D(): index out of range", {}); + return doMesh2D(id); +} + +MeshData2D* AbstractImporter::doMesh2D(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::mesh3DCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh3DCount(): no file opened", {}); + return doMesh3DCount(); +} + +UnsignedInt AbstractImporter::doMesh3DCount() const { return 0; } + +Int AbstractImporter::mesh3DForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh3DForName(): no file opened", {}); + return doMesh3DForName(name); +} + +Int AbstractImporter::doMesh3DForName(const std::string&) { return -1; } + +std::string AbstractImporter::mesh3DName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh3DName(): no file opened", {}); + CORRADE_ASSERT(id < doMesh3DCount(), "Trade::AbstractImporter::object3DName(): index out of range", {}); + return doMesh3DName(id); +} + +std::string AbstractImporter::doMesh3DName(UnsignedInt) { return {}; } + +MeshData3D* AbstractImporter::mesh3D(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::mesh3D(): no file opened", {}); + CORRADE_ASSERT(id < doMesh3DCount(), "Trade::AbstractImporter::mesh3D(): index out of range", {}); + return doMesh3D(id); +} + +MeshData3D* AbstractImporter::doMesh3D(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::materialCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::materialCount(): no file opened", {}); + return doMaterialCount(); +} + +UnsignedInt AbstractImporter::doMaterialCount() const { return 0; } + +Int AbstractImporter::materialForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::materialForName(): no file opened", {}); + return doMaterialForName(name); +} + +Int AbstractImporter::doMaterialForName(const std::string&) { return -1; } + +std::string AbstractImporter::materialName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::materialName(): no file opened", {}); + CORRADE_ASSERT(id < doMaterialCount(), "Trade::AbstractImporter::materialName(): index out of range", {}); + return doMaterialName(id); +} + +std::string AbstractImporter::doMaterialName(UnsignedInt) { return {}; } + +AbstractMaterialData* AbstractImporter::material(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::material(): no file opened", {}); + CORRADE_ASSERT(id < doMaterialCount(), "Trade::AbstractImporter::material(): index out of range", {}); + return doMaterial(id); +} + +AbstractMaterialData* AbstractImporter::doMaterial(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::textureCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::textureCount(): no file opened", {}); + return doTextureCount(); +} + +UnsignedInt AbstractImporter::doTextureCount() const { return 0; } + +Int AbstractImporter::textureForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::textureForName(): no file opened", {}); + return doTextureForName(name); +} + +Int AbstractImporter::doTextureForName(const std::string&) { return -1; } + +std::string AbstractImporter::textureName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::textureName(): no file opened", {}); + CORRADE_ASSERT(id < doTextureCount(), "Trade::AbstractImporter::textureName(): index out of range", {}); + return doTextureName(id); +} + +std::string AbstractImporter::doTextureName(UnsignedInt) { return {}; } + +TextureData* AbstractImporter::texture(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::texture(): no file opened", {}); + CORRADE_ASSERT(id < doTextureCount(), "Trade::AbstractImporter::texture(): index out of range", {}); + return doTexture(id); +} + +TextureData* AbstractImporter::doTexture(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::image1DCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image1DCount(): no file opened", {}); + return doImage1DCount(); +} + +UnsignedInt AbstractImporter::doImage1DCount() const { return 0; } + +Int AbstractImporter::image1DForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image1DForName(): no file opened", {}); + return doImage1DForName(name); +} + +Int AbstractImporter::doImage1DForName(const std::string&) { return -1; } + +std::string AbstractImporter::image1DName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image1DName(): no file opened", {}); + CORRADE_ASSERT(id < doImage1DCount(), "Trade::AbstractImporter::image1DName(): index out of range", {}); + return doImage1DName(id); +} + +std::string AbstractImporter::doImage1DName(UnsignedInt) { return {}; } + +ImageData1D* AbstractImporter::image1D(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image1D(): no file opened", {}); + CORRADE_ASSERT(id < doImage1DCount(), "Trade::AbstractImporter::image1D(): index out of range", {}); + return doImage1D(id); +} + +ImageData1D* AbstractImporter::doImage1D(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::image2DCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image2DCount(): no file opened", {}); + return doImage2DCount(); +} + +UnsignedInt AbstractImporter::doImage2DCount() const { return 0; } + +Int AbstractImporter::image2DForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image1DForName(): no file opened", {}); + return doImage2DForName(name); +} + +Int AbstractImporter::doImage2DForName(const std::string&) { return -1; } + +std::string AbstractImporter::image2DName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image2DName(): no file opened", {}); + CORRADE_ASSERT(id < doImage2DCount(), "Trade::AbstractImporter::image2DName(): index out of range", {}); + return doImage2DName(id); +} + +std::string AbstractImporter::doImage2DName(UnsignedInt) { return {}; } + +ImageData2D* AbstractImporter::image2D(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image2D(): no file opened", {}); + CORRADE_ASSERT(id < doImage2DCount(), "Trade::AbstractImporter::image2D(): index out of range", {}); + return doImage2D(id); +} + +ImageData2D* AbstractImporter::doImage2D(UnsignedInt) { return nullptr; } + +UnsignedInt AbstractImporter::image3DCount() const { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image3DCount(): no file opened", {}); + return doImage3DCount(); +} + +UnsignedInt AbstractImporter::doImage3DCount() const { return 0; } + +Int AbstractImporter::image3DForName(const std::string& name) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image3DForName(): no file opened", {}); + return doImage3DForName(name); +} + +Int AbstractImporter::doImage3DForName(const std::string&) { return -1; } + +std::string AbstractImporter::image3DName(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image3DName(): no file opened", {}); + CORRADE_ASSERT(id < image3DCount(), "Trade::AbstractImporter::image3DName(): index out of range", {}); + return doImage3DName(id); +} + +std::string AbstractImporter::doImage3DName(UnsignedInt) { return {}; } + +ImageData3D* AbstractImporter::image3D(const UnsignedInt id) { + CORRADE_ASSERT(isOpened(), "Trade::AbstractImporter::image3D(): no file opened", {}); + CORRADE_ASSERT(id < doImage3DCount(), "Trade::AbstractImporter::image3D(): index out of range", {}); + return doImage3D(id); +} + +ImageData3D* AbstractImporter::doImage3D(UnsignedInt) { return nullptr; } }} diff --git a/src/Trade/AbstractImporter.h b/src/Trade/AbstractImporter.h index 85dde6362..a82d57a3c 100644 --- a/src/Trade/AbstractImporter.h +++ b/src/Trade/AbstractImporter.h @@ -44,17 +44,31 @@ Importer is used for importing data like scenes, lights, objects, images, textures etc. @section AbstractImporter-subclassing Subclassing -Plugin implements function features(), one or more open() functions, -function close() and one or more pairs of data access functions, based on -which features are supported in given format. + +Plugin implements function doFeatures(), doIsOpened(), one of or both +doOpenData() and doOpenFile() functions, function doClose() and one or more +tuples of data access functions, based on which features are supported in given +format. For multi-data formats file opening shouldn't take long, all parsing should be done in data parsing functions, because the user might want to import only some data. This is obviously not the case for single-data formats like images, as the file contains all data user wants to import. + +You don't need to do most of the redundant sanity checks, these things are +checked by the implementation: + +- Functions doOpenData() and doOpenFile() are called after the previous file + was closed, function doClose() is called only if there is any file opened. +- Function doOpenData() is called only if @ref Feature "Feature::OpenData" + is supported. +- All `do*()` implementations working on opened file are called only if + there is any file opened. +- All `do*()` implementations taking data ID as parameter are called only if + the ID is from valid range. */ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { - CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Trade.AbstractImporter/0.2.1") + CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Trade.AbstractImporter/0.3") public: /** @@ -63,8 +77,8 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * @see Features, features() */ enum class Feature: UnsignedByte { - OpenData = 1 << 0, /**< Opening files from raw data */ - OpenFile = 1 << 1 /**< Opening files specified by filename */ + /** Opening files from raw data using openData() */ + OpenData = 1 << 0 }; /** @brief Set of features supported by this importer */ @@ -77,7 +91,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { explicit AbstractImporter(PluginManager::AbstractManager* manager, std::string plugin); /** @brief Features supported by this importer */ - virtual Features features() const = 0; + Features features() const { return doFeatures(); } + + /** @brief Whether any file is opened */ + bool isOpened() const { return doIsOpened(); } /** * @brief Open raw data @@ -89,35 +106,23 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * supported. Returns `true` on success, `false` otherwise. * @see features(), openFile() */ - virtual bool openData(const void* const data, const std::size_t size); - - /** - * @brief Open raw data - * @param data Data - * - * Convenience alternative to above function useful when array size is - * known at compile-time. - */ - template bool openData(const T(&data)[size]) { - return openData(data, size*sizeof(T)); - } + bool openData(Containers::ArrayReference data); /** * @brief Open file * @param filename Filename * * Closes previous file, if it was opened, and tries to open given - * file. Available only if @ref Feature "Feature::OpenFile" is - * supported. Returns `true` on success, `false` otherwise. + * file. Returns `true` on success, `false` otherwise. * @see features(), openData() */ - virtual bool openFile(const std::string& filename); + bool openFile(const std::string& filename); /** @brief Close file */ - virtual void close() = 0; + void close(); /** @{ @name Data accessors - * Each function pair provides access to the data. + * Each function tuple provides access to given data. */ /** @@ -129,10 +134,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * @note The function is not const, because the value will probably * be lazy-populated. */ - virtual Int defaultScene() { return -1; } + Int defaultScene(); /** @brief %Scene count */ - virtual UnsignedInt sceneCount() const { return 0; } + UnsignedInt sceneCount() const; /** * @brief %Scene ID for given name @@ -140,7 +145,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no scene for given name exists, returns `-1`. * @see sceneName() */ - virtual Int sceneForName(const std::string& name); + Int sceneForName(const std::string& name); /** * @brief %Scene name @@ -148,7 +153,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see sceneForName() */ - virtual std::string sceneName(UnsignedInt id); + std::string sceneName(UnsignedInt id); /** * @brief %Scene @@ -157,10 +162,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given scene or `nullptr` if import failed. Deleting the data * is user responsibility. */ - virtual SceneData* scene(UnsignedInt id); + SceneData* scene(UnsignedInt id); /** @brief %Light count */ - virtual UnsignedInt lightCount() const { return 0; } + UnsignedInt lightCount() const; /** * @brief %Light ID for given name @@ -168,7 +173,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no light for given name exists, returns `-1`. * @see lightName() */ - virtual Int lightForName(const std::string& name); + Int lightForName(const std::string& name); /** * @brief %Light name @@ -176,7 +181,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see lightForName() */ - virtual std::string lightName(UnsignedInt id); + std::string lightName(UnsignedInt id); /** * @brief %Light @@ -185,10 +190,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given light or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual LightData* light(UnsignedInt id); + LightData* light(UnsignedInt id); /** @brief Camera count */ - virtual UnsignedInt cameraCount() const { return 0; } + UnsignedInt cameraCount() const; /** * @brief Camera ID for given name @@ -196,7 +201,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no camera for given name exists, returns `-1`. * @see cameraName() */ - virtual Int cameraForName(const std::string& name); + Int cameraForName(const std::string& name); /** * @brief Camera name @@ -204,7 +209,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see cameraForName() */ - virtual std::string cameraName(UnsignedInt id); + std::string cameraName(UnsignedInt id); /** * @brief Camera @@ -213,10 +218,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given camera or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual CameraData* camera(UnsignedInt id); + CameraData* camera(UnsignedInt id); /** @brief Two-dimensional object count */ - virtual UnsignedInt object2DCount() const { return 0; } + UnsignedInt object2DCount() const; /** * @brief Two-dimensional object ID for given name @@ -224,7 +229,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no scene for given name exists, returns `-1`. * @see object2DName() */ - virtual Int object2DForName(const std::string& name); + Int object2DForName(const std::string& name); /** * @brief Two-dimensional object name @@ -232,7 +237,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see object2DForName() */ - virtual std::string object2DName(UnsignedInt id); + std::string object2DName(UnsignedInt id); /** * @brief Two-dimensional object @@ -241,10 +246,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given object or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual ObjectData2D* object2D(UnsignedInt id); + ObjectData2D* object2D(UnsignedInt id); /** @brief Three-dimensional object count */ - virtual UnsignedInt object3DCount() const { return 0; } + UnsignedInt object3DCount() const; /** * @brief Three-dimensional object ID for given name @@ -252,7 +257,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no scene for given name exists, returns `-1`. * @see object3DName() */ - virtual Int object3DForName(const std::string& name); + Int object3DForName(const std::string& name); /** * @brief Three-dimensional object name @@ -260,7 +265,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see object3DForName() */ - virtual std::string object3DName(UnsignedInt id); + std::string object3DName(UnsignedInt id); /** * @brief Three-dimensional object @@ -269,10 +274,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given object or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual ObjectData3D* object3D(UnsignedInt id); + ObjectData3D* object3D(UnsignedInt id); /** @brief Two-dimensional mesh count */ - virtual UnsignedInt mesh2DCount() const { return 0; } + UnsignedInt mesh2DCount() const; /** * @brief Two-dimensional mesh ID for given name @@ -280,7 +285,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no mesh for given name exists, returns `-1`. * @see mesh2DName() */ - virtual Int mesh2DForName(const std::string& name); + Int mesh2DForName(const std::string& name); /** * @brief Two-dimensional mesh name @@ -288,7 +293,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see mesh2DForName() */ - virtual std::string mesh2DName(UnsignedInt id); + std::string mesh2DName(UnsignedInt id); /** * @brief Two-dimensional mesh @@ -297,10 +302,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given mesh or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual MeshData2D* mesh2D(UnsignedInt id); + MeshData2D* mesh2D(UnsignedInt id); /** @brief Three-dimensional mesh count */ - virtual UnsignedInt mesh3DCount() const { return 0; } + UnsignedInt mesh3DCount() const; /** * @brief Three-dimensional mesh ID for given name @@ -308,7 +313,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no mesh for given name exists, returns `-1`. * @see mesh3DName() */ - virtual Int mesh3DForName(const std::string& name); + Int mesh3DForName(const std::string& name); /** * @brief Three-dimensional mesh name @@ -316,7 +321,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see mesh3DForName() */ - virtual std::string mesh3DName(UnsignedInt id); + std::string mesh3DName(UnsignedInt id); /** * @brief Three-dimensional mesh @@ -325,10 +330,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given mesh or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual MeshData3D* mesh3D(UnsignedInt id); + MeshData3D* mesh3D(UnsignedInt id); /** @brief Material count */ - virtual UnsignedInt materialCount() const { return 0; } + UnsignedInt materialCount() const; /** * @brief Material ID for given name @@ -336,7 +341,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no material for given name exists, returns `-1`. * @see materialName() */ - virtual Int materialForName(const std::string& name); + Int materialForName(const std::string& name); /** * @brief Material name @@ -344,7 +349,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see materialForName() */ - virtual std::string materialName(UnsignedInt id); + std::string materialName(UnsignedInt id); /** * @brief Material @@ -353,10 +358,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given material or `nullptr` if importing failed. Deleting * the data is user responsibility. */ - virtual AbstractMaterialData* material(UnsignedInt id); + AbstractMaterialData* material(UnsignedInt id); /** @brief %Texture count */ - virtual UnsignedInt textureCount() const { return 0; } + UnsignedInt textureCount() const; /** * @brief %Texture ID for given name @@ -364,7 +369,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no texture for given name exists, returns `-1`. * @see textureName() */ - virtual Int textureForName(const std::string& name); + Int textureForName(const std::string& name); /** * @brief %Texture name @@ -372,7 +377,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see textureForName() */ - virtual std::string textureName(UnsignedInt id); + std::string textureName(UnsignedInt id); /** * @brief %Texture @@ -381,10 +386,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given texture or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual TextureData* texture(UnsignedInt id); + TextureData* texture(UnsignedInt id); /** @brief One-dimensional image count */ - virtual UnsignedInt image1DCount() const { return 0; } + UnsignedInt image1DCount() const; /** * @brief One-dimensional image ID for given name @@ -392,7 +397,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no image for given name exists, returns `-1`. * @see image1Dname() */ - virtual Int image1DForName(const std::string& name); + Int image1DForName(const std::string& name); /** * @brief One-dimensional image name @@ -400,7 +405,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see image1DForName() */ - virtual std::string image1DName(UnsignedInt id); + std::string image1DName(UnsignedInt id); /** * @brief One-dimensional image @@ -409,10 +414,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given image or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual ImageData1D* image1D(UnsignedInt id); + ImageData1D* image1D(UnsignedInt id); /** @brief Two-dimensional image count */ - virtual UnsignedInt image2DCount() const { return 0; } + UnsignedInt image2DCount() const; /** * @brief Two-dimensional image ID for given name @@ -420,7 +425,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no image for given name exists, returns `-1`. * @see image2DName() */ - virtual Int image2DForName(const std::string& name); + Int image2DForName(const std::string& name); /** * @brief Two-dimensional image name @@ -428,7 +433,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see image2DForName() */ - virtual std::string image2DName(UnsignedInt id); + std::string image2DName(UnsignedInt id); /** * @brief Two-dimensional image @@ -437,10 +442,10 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given image or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual ImageData2D* image2D(UnsignedInt id); + ImageData2D* image2D(UnsignedInt id); /** @brief Three-dimensional image count */ - virtual UnsignedInt image3DCount() const { return 0; } + UnsignedInt image3DCount() const; /** * @brief Three-dimensional image ID for given name @@ -448,7 +453,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * If no image for given name exists, returns `-1`. * @see image3DName() */ - virtual Int image3DForName(const std::string& name); + Int image3DForName(const std::string& name); /** * @brief Three-dimensional image name @@ -456,7 +461,7 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * * @see image3DForName() */ - virtual std::string image3DName(UnsignedInt id); + std::string image3DName(UnsignedInt id); /** * @brief Three-dimensional image @@ -465,9 +470,182 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { * Returns given image or `nullptr` if importing failed. Deleting the * data is user responsibility. */ - virtual ImageData3D* image3D(UnsignedInt id); + ImageData3D* image3D(UnsignedInt id); /*@}*/ + + #ifndef DOXYGEN_GENERATING_OUTPUT + private: + #else + protected: + #endif + /** @brief Implementation for features() */ + virtual Features doFeatures() const = 0; + + /** @brief Implementation for isOpened() */ + virtual bool doIsOpened() const = 0; + + /** @brief Implementation for openData() */ + virtual void doOpenData(Containers::ArrayReference data); + + /** + * @brief Implementation for openFile() + * + * If @ref Feature "Feature::OpenData" is supported, default + * implementation opens the file and calls doOpenData() with its + * contents. + */ + virtual void doOpenFile(const std::string& filename); + + /** @brief Implementation for close() */ + virtual void doClose() = 0; + + /** @brief Implementation for defaultScene() */ + virtual Int doDefaultScene(); + + /** @brief Implementation for sceneCount() */ + virtual UnsignedInt doSceneCount() const; + + /** @brief Implementation for sceneForName() */ + virtual Int doSceneForName(const std::string& name); + + /** @brief Implementation for sceneName() */ + virtual std::string doSceneName(UnsignedInt id); + + /** @brief Implementation for scene() */ + virtual SceneData* doScene(UnsignedInt id); + + /** @brief Implementation for lightCount() */ + virtual UnsignedInt doLightCount() const; + + /** @brief Implementation for lightForName() */ + virtual Int doLightForName(const std::string& name); + + /** @brief Implementation for lightName() */ + virtual std::string doLightName(UnsignedInt id); + + /** @brief Implementation for light() */ + virtual LightData* doLight(UnsignedInt id); + + /** @brief Implementation for cameraCount() */ + virtual UnsignedInt doCameraCount() const; + + /** @brief Implementation for cameraForName() */ + virtual Int doCameraForName(const std::string& name); + + /** @brief Implementation for cameraName() */ + virtual std::string doCameraName(UnsignedInt id); + + /** @brief Implementation for camera() */ + virtual CameraData* doCamera(UnsignedInt id); + + /** @brief Implementation for object2DCount() */ + virtual UnsignedInt doObject2DCount() const; + + /** @brief Implementation for object2DForName() */ + virtual Int doObject2DForName(const std::string& name); + + /** @brief Implementation for object2DName() */ + virtual std::string doObject2DName(UnsignedInt id); + + /** @brief Implementation for object2D() */ + virtual ObjectData2D* doObject2D(UnsignedInt id); + + /** @brief Implementation for object3DCount() */ + virtual UnsignedInt doObject3DCount() const; + + /** @brief Implementation for object3DForName() */ + virtual Int doObject3DForName(const std::string& name); + + /** @brief Implementation for object3DName() */ + virtual std::string doObject3DName(UnsignedInt id); + + /** @brief Implementation for object3D() */ + virtual ObjectData3D* doObject3D(UnsignedInt id); + + /** @brief Implementation for mesh2DCount() */ + virtual UnsignedInt doMesh2DCount() const; + + /** @brief Implementation for mesh2DForName() */ + virtual Int doMesh2DForName(const std::string& name); + + /** @brief Implementation for mesh2DName() */ + virtual std::string doMesh2DName(UnsignedInt id); + + /** @brief Implementation for mesh2D() */ + virtual MeshData2D* doMesh2D(UnsignedInt id); + + /** @brief Implementation for mesh3DCount() */ + virtual UnsignedInt doMesh3DCount() const; + + /** @brief Implementation for mesh3DForName() */ + virtual Int doMesh3DForName(const std::string& name); + + /** @brief Implementation for mesh3DName() */ + virtual std::string doMesh3DName(UnsignedInt id); + + /** @brief Implementation for mesh3D() */ + virtual MeshData3D* doMesh3D(UnsignedInt id); + + /** @brief Implementation for materialCount() */ + virtual UnsignedInt doMaterialCount() const; + + /** @brief Implementation for materialForName() */ + virtual Int doMaterialForName(const std::string& name); + + /** @brief Implementation for materialName() */ + virtual std::string doMaterialName(UnsignedInt id); + + /** @brief Implementation for material() */ + virtual AbstractMaterialData* doMaterial(UnsignedInt id); + + /** @brief Implementation for textureCount() */ + virtual UnsignedInt doTextureCount() const; + + /** @brief Implementation for textureForName() */ + virtual Int doTextureForName(const std::string& name); + + /** @brief Implementation for textureName() */ + virtual std::string doTextureName(UnsignedInt id); + + /** @brief Implementation for texture() */ + virtual TextureData* doTexture(UnsignedInt id); + + /** @brief Implementation for image1DCount() */ + virtual UnsignedInt doImage1DCount() const; + + /** @brief Implementation for image1DForName() */ + virtual Int doImage1DForName(const std::string& name); + + /** @brief Implementation for image1DName() */ + virtual std::string doImage1DName(UnsignedInt id); + + /** @brief Implementation for image1D() */ + virtual ImageData1D* doImage1D(UnsignedInt id); + + /** @brief Implementation for image2DCount() */ + virtual UnsignedInt doImage2DCount() const; + + /** @brief Implementation for image2DForName() */ + virtual Int doImage2DForName(const std::string& name); + + /** @brief Implementation for image2DName() */ + virtual std::string doImage2DName(UnsignedInt id); + + /** @brief Implementation for image2D() */ + virtual ImageData2D* doImage2D(UnsignedInt id); + + /** @brief Implementation for image3DCount() */ + virtual UnsignedInt doImage3DCount() const; + + /** @brief Implementation for image3DForName() */ + virtual Int doImage3DForName(const std::string& name); + + /** @brief Implementation for image3DName() */ + virtual std::string doImage3DName(UnsignedInt id); + + /** @brief Implementation for image3D() */ + virtual ImageData3D* doImage3D(UnsignedInt id); }; CORRADE_ENUMSET_OPERATORS(AbstractImporter::Features) diff --git a/src/Trade/Test/AbstractImporterTest.cpp b/src/Trade/Test/AbstractImporterTest.cpp new file mode 100644 index 000000000..0e1143d9e --- /dev/null +++ b/src/Trade/Test/AbstractImporterTest.cpp @@ -0,0 +1,69 @@ +/* + 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 "Trade/AbstractImporter.h" + +#include "testConfigure.h" + +namespace Magnum { namespace Trade { namespace Test { + +class AbstractImporterTest: public TestSuite::Tester { + public: + explicit AbstractImporterTest(); + + void openFile(); +}; + +AbstractImporterTest::AbstractImporterTest() { + addTests({&AbstractImporterTest::openFile}); +} + +void AbstractImporterTest::openFile() { + class DataImporter: public Trade::AbstractImporter { + private: + Features doFeatures() const override { return Feature::OpenData; } + bool doIsOpened() const override { return opened; } + void doClose() override {} + + void doOpenData(Containers::ArrayReference data) override { + opened = (data.size() == 1 && data[0] == 0xa5); + } + + bool opened; + }; + + /* doOpenFile() should call doOpenData() */ + DataImporter importer; + CORRADE_VERIFY(!importer.isOpened()); + importer.openFile(Utility::Directory::join(TRADE_TEST_DIR, "file.bin")); + CORRADE_VERIFY(importer.isOpened()); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::AbstractImporterTest) diff --git a/src/Trade/Test/CMakeLists.txt b/src/Trade/Test/CMakeLists.txt index 1581379f4..7d9c878e0 100644 --- a/src/Trade/Test/CMakeLists.txt +++ b/src/Trade/Test/CMakeLists.txt @@ -22,5 +22,11 @@ # DEALINGS IN THE SOFTWARE. # +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testConfigure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/testConfigure.h) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +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/file.bin b/src/Trade/Test/file.bin new file mode 100644 index 000000000..dd6737691 --- /dev/null +++ b/src/Trade/Test/file.bin @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Trade/Test/testConfigure.h.cmake b/src/Trade/Test/testConfigure.h.cmake new file mode 100644 index 000000000..921a711c0 --- /dev/null +++ b/src/Trade/Test/testConfigure.h.cmake @@ -0,0 +1,25 @@ +/* + 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. +*/ + +#define TRADE_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}" 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 03/36] 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}" From d655352485f6817dedcc9693230f88f671ef8fa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 Jun 2013 17:54:23 +0200 Subject: [PATCH 04/36] Text: reworked AbstractFont plugin interface. Implementation is moved into `do*()` private virtual functions, public interface is doing additional sanity checks around them. Opening files is by default done in base implementation, which loads the files into memory and then calls function for opening raw data. Added interface for opening multi-file fonts from raw data, default implementation just calls single-file implementation. Function for creating glyph cache converts the text from UTF-8 to UTF-32 and then calls the implementation, removing the burden from reimplementing this in each plugin. Added unit test for file and single data opening, bumped plugin version to 0.2. --- src/Text/AbstractFont.cpp | 96 ++++++++++++++++++++- src/Text/AbstractFont.h | 128 ++++++++++++++++++++++++---- src/Text/CMakeLists.txt | 4 + src/Text/Test/AbstractFontTest.cpp | 118 +++++++++++++++++++++++++ src/Text/Test/CMakeLists.txt | 30 +++++++ src/Text/Test/data.bin | 1 + src/Text/Test/testConfigure.h.cmake | 25 ++++++ 7 files changed, 386 insertions(+), 16 deletions(-) create mode 100644 src/Text/Test/AbstractFontTest.cpp create mode 100644 src/Text/Test/CMakeLists.txt create mode 100644 src/Text/Test/data.bin create mode 100644 src/Text/Test/testConfigure.h.cmake diff --git a/src/Text/AbstractFont.cpp b/src/Text/AbstractFont.cpp index 58a90cf2b..741d1b48c 100644 --- a/src/Text/AbstractFont.cpp +++ b/src/Text/AbstractFont.cpp @@ -22,7 +22,11 @@ DEALINGS IN THE SOFTWARE. */ -#include "Text/AbstractFont.h" +#include "AbstractFont.h" + +#include +#include +#include namespace Magnum { namespace Text { @@ -30,6 +34,96 @@ AbstractFont::AbstractFont(): _size(0.0f) {} AbstractFont::AbstractFont(PluginManager::AbstractManager* manager, std::string plugin): AbstractPlugin(manager, std::move(plugin)), _size(0.0f) {} +bool AbstractFont::openData(const std::vector>>& data, const Float size) { + CORRADE_ASSERT(features() & Feature::OpenData, + "Text::AbstractFont::openData(): feature not supported", false); + CORRADE_ASSERT(!data.empty(), + "Text::AbstractFont::openData(): no data passed", false); + + close(); + doOpenData(data, size); + return isOpened(); +} + +void AbstractFont::doOpenData(const std::vector>>& data, const Float size) { + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFont::openData(): feature advertised but not implemented", ); + CORRADE_ASSERT(data.size() == 1, + "Text::AbstractFont::openData(): expected just one file for single-file format", ); + + close(); + doOpenSingleData(data[0].second, size); +} + +bool AbstractFont::openSingleData(const Containers::ArrayReference data, const Float size) { + CORRADE_ASSERT(features() & Feature::OpenData, + "Text::AbstractFont::openSingleData(): feature not supported", false); + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFont::openSingleData(): the format is not single-file", false); + + close(); + doOpenSingleData(data, size); + return isOpened(); +} + +void AbstractFont::doOpenSingleData(Containers::ArrayReference, Float) { + CORRADE_ASSERT(false, "Text::AbstractFont::openSingleData(): feature advertised but not implemented", ); +} + +bool AbstractFont::openFile(const std::string& filename, const Float size) { + close(); + doOpenFile(filename, size); + return isOpened(); +} + +void AbstractFont::doOpenFile(const std::string& filename, const Float size) { + CORRADE_ASSERT(features() & Feature::OpenData && !(features() & Feature::MultiFile), + "Text::AbstractFont::openFile(): not implemented", ); + + /* Open file */ + std::ifstream in(filename.data(), std::ios::binary); + if(!in.good()) { + Error() << "Trade::AbstractFont::openFile(): cannot open file" << filename; + return; + } + + /* Create array to hold file contents */ + in.seekg(0, std::ios::end); + Containers::Array data(in.tellg()); + + /* Read data, close */ + in.seekg(0, std::ios::beg); + in.read(reinterpret_cast(data.begin()), data.size()); + in.close(); + + doOpenSingleData(data, size); +} + +void AbstractFont::close() { + if(isOpened()) doClose(); +} + +void AbstractFont::createGlyphCache(GlyphCache* const cache, const std::string& characters) { + CORRADE_ASSERT(isOpened(), "Text::AbstractFont::createGlyphCache(): no font opened", ); + + /* Get glyph codes from characters */ + std::u32string unicodeCharacters; + unicodeCharacters.reserve(characters.size()+1); + for(std::size_t i = 0; i != characters.size(); ) { + char32_t unicode; + std::tie(unicode, i) = Utility::Unicode::nextChar(characters, i); + unicodeCharacters.push_back(unicode); + } + + doCreateGlyphCache(cache, unicodeCharacters); +} + +AbstractLayouter* AbstractFont::layout(const GlyphCache* const cache, const Float size, const std::string& text) { + CORRADE_ASSERT(isOpened(), "Text::AbstractFont::layout(): no font opened", nullptr); + + return doLayout(cache, size, text); +} + AbstractLayouter::AbstractLayouter(): _glyphCount(0) {} AbstractLayouter::~AbstractLayouter() {} diff --git a/src/Text/AbstractFont.h b/src/Text/AbstractFont.h index 765defb49..de2f4cf45 100644 --- a/src/Text/AbstractFont.h +++ b/src/Text/AbstractFont.h @@ -50,41 +50,93 @@ information. See TextRenderer for information about text rendering. @section AbstractFont-subclassing Subclassing -Plugin implements functions open(), close(), createGlyphCache() and layout(). +Plugin implements doFeatures(), doClose(), doCreateGlyphCache(), doLayout() and +one or more of `doOpen*()` functions. + +You don't need to do most of the redundant sanity checks, these things are +checked by the implementation: + +- Functions doOpenData(), doOpenSingleData() and doOpenFile() are called + after the previous file was closed, function doClose() is called only if + there is any file opened. +- Functions doOpenData() and doOpenSingleData() are called only if + @ref Feature "Feature::OpenData" is supported. +- All `do*()` implementations working on opened file are called only if + there is any file opened. */ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { - CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Text.AbstractFont/0.1") + CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Text.AbstractFont/0.2") public: + /** + * @brief Features supported by this importer + * + * @see Features, features() + */ + enum class Feature: UnsignedByte { + /** Opening fonts from raw data using openData() */ + OpenData = 1 << 0, + + /** + * The format is multi-file, thus openSingleData() convenience + * function cannot be used. + */ + MultiFile = 1 << 1 + }; + + /** @brief Set of features supported by this importer */ + typedef Containers::EnumSet Features; + /** @brief Default constructor */ explicit AbstractFont(); /** @brief Plugin manager constructor */ explicit AbstractFont(PluginManager::AbstractManager* manager, std::string plugin); + /** @brief Features supported by this font */ + Features features() const { return doFeatures(); } + + /** @brief Whether any file is opened */ + bool isOpened() const { return doIsOpened(); } + /** - * @brief Open font from file - * @param filename Font file + * @brief Open font from raw data + * @param data Pairs of filename and file data + * @param size Font size + * + * Closes previous file, if it was opened, and tries to open given + * file. Available only if @ref Feature "Feature::OpenData" is + * supported. Returns `true` on success, `false` otherwise. + */ + bool openData(const std::vector>>& data, Float size); + + /** + * @brief Open font from single data + * @param data File data * @param size Font size * * Closes previous file, if it was opened, and tries to open given - * file. Returns `true` on success, `false` otherwise. + * file. Available only if @ref Feature "Feature::OpenData" is + * supported and the plugin doesn't have @ref Feature "Feature::MultiFile". + * Returns `true` on success, `false` otherwise. */ - virtual bool open(const std::string& filename, Float size) = 0; + bool openSingleData(Containers::ArrayReference data, Float size); /** - * @brief Open font from memory - * @param data Font data - * @param dataSize Font data size + * @brief Open font from file + * @param filename Font file * @param size Font size * * Closes previous file, if it was opened, and tries to open given - * file. Returns `true` on success, `false` otherwise. + * file. If the plugin has @ref Feature "Feature::MultiFile", the + * function will use additional files in given path, all sharing common + * basename derived from @p filename. Returns `true` on success, + * `false` otherwise. */ - virtual bool open(const unsigned char* data, std::size_t dataSize, Float size) = 0; + bool openFile(const std::string& filename, Float size); /** @brief Close font */ - virtual void close() = 0; + void close(); /** @brief Font size */ Float size() const { return _size; } @@ -96,17 +148,17 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * * Fills the cache with given characters. */ - virtual void createGlyphCache(GlyphCache* cache, const std::string& characters) = 0; + void createGlyphCache(GlyphCache* cache, const std::string& characters); /** - * @brief Layout the text using font own layouter + * @brief Layout the text using font's own layouter * @param cache Glyph cache * @param size Font size * @param text %Text to layout * * @see createGlyphCache() */ - virtual AbstractLayouter* layout(const GlyphCache* cache, Float size, const std::string& text) = 0; + AbstractLayouter* layout(const GlyphCache* cache, Float size, const std::string& text); #ifdef DOXYGEN_GENERATING_OUTPUT private: @@ -114,6 +166,52 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { protected: #endif Float _size; + + #ifdef DOXYGEN_GENERATING_OUTPUT + protected: + #else + private: + #endif + /** @brief Implementation for features() */ + virtual Features doFeatures() const = 0; + + /** @brief Implementation for isOpened() */ + virtual bool doIsOpened() const = 0; + + /** + * @brief Implementation for openData() + * + * If the plugin doesn't have @ref Feature "Feature::MultiFile", + * default implementation calls doOpenSingleData(). + */ + virtual void doOpenData(const std::vector>>& data, Float size); + + /** @brief Implementation for openSingleData() */ + virtual void doOpenSingleData(Containers::ArrayReference data, Float size); + + /** + * @brief Implementation for openFile() + * + * If @ref Feature "Feature::OpenData" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile", default + * implementation opens the file and calls doOpenSingleData() with its + * contents. + */ + virtual void doOpenFile(const std::string& filename, Float size); + + /** @brief Implementation for close() */ + virtual void doClose() = 0; + + /** + * @brief Implementation for createGlyphCache() + * + * The string is converted from UTF-8 to UTF-32, unique characters are + * *not* removed. + */ + virtual void doCreateGlyphCache(GlyphCache* cache, const std::u32string& characters) = 0; + + /** @brief Implementation for layout() */ + virtual AbstractLayouter* doLayout(const GlyphCache* cache, Float size, const std::string& text) = 0; }; /** diff --git a/src/Text/CMakeLists.txt b/src/Text/CMakeLists.txt index fa28db8d1..b7e7f30a2 100644 --- a/src/Text/CMakeLists.txt +++ b/src/Text/CMakeLists.txt @@ -45,3 +45,7 @@ target_link_libraries(MagnumText Magnum MagnumTextureTools) install(TARGETS MagnumText DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}) install(FILES ${MagnumText_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Text) + +if(BUILD_TESTS) + add_subdirectory(Test) +endif() diff --git a/src/Text/Test/AbstractFontTest.cpp b/src/Text/Test/AbstractFontTest.cpp new file mode 100644 index 000000000..4447b9aaa --- /dev/null +++ b/src/Text/Test/AbstractFontTest.cpp @@ -0,0 +1,118 @@ +/* + 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 "Text/AbstractFont.h" + +#include "testConfigure.h" + +namespace Magnum { namespace Text { namespace Test { + +class AbstractFontTest: public TestSuite::Tester { + public: + explicit AbstractFontTest(); + + void openSingleData(); + void openFile(); + + void createGlyphCache(); +}; + +AbstractFontTest::AbstractFontTest() { + addTests({&AbstractFontTest::openSingleData, + &AbstractFontTest::openFile, + + &AbstractFontTest::createGlyphCache}); +} + +namespace { + +class SingleDataFont: public Text::AbstractFont { + public: + Features doFeatures() const override { return Feature::OpenData; } + bool doIsOpened() const override { return opened; } + void doClose() override {} + + void doOpenSingleData(const Containers::ArrayReference data, Float) override { + opened = (data.size() == 1 && data[0] == 0xa5); + } + + void doCreateGlyphCache(GlyphCache*, const std::u32string&) override {} + + AbstractLayouter* doLayout(const GlyphCache*, Float, const std::string&) { + return nullptr; + } + + bool opened = false; +}; + +} + +void AbstractFontTest::openSingleData() { + /* doOpenData() should call doOpenSingleData() */ + SingleDataFont font; + const unsigned char data[] = {0xa5}; + CORRADE_VERIFY(!font.isOpened()); + font.openData({{{}, data}}, 3.0f); + CORRADE_VERIFY(font.isOpened()); +} + +void AbstractFontTest::openFile() { + /* doOpenFile() should call doOpenSingleData() */ + SingleDataFont font; + CORRADE_VERIFY(!font.isOpened()); + font.openFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin"), 3.0f); + CORRADE_VERIFY(font.isOpened()); +} + +void AbstractFontTest::createGlyphCache() { + class CachingFont: public Text::AbstractFont { + public: + std::u32string cacheCharacters; + + private: + Features doFeatures() const override { return {}; } + bool doIsOpened() const override { return true; } + void doClose() override {} + + void doCreateGlyphCache(GlyphCache*, const std::u32string& characters) override { + cacheCharacters = characters; + } + + AbstractLayouter* doLayout(const GlyphCache*, Float, const std::string&) override { + return nullptr; + } + }; + + CachingFont font; + font.createGlyphCache(nullptr, "žluťoučký kůň"); + CORRADE_COMPARE(font.cacheCharacters, U"\u017Elu\u0165ou\u010Dk\u00FD k\u016F\u0148"); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractFontTest) diff --git a/src/Text/Test/CMakeLists.txt b/src/Text/Test/CMakeLists.txt new file mode 100644 index 000000000..e190c7785 --- /dev/null +++ b/src/Text/Test/CMakeLists.txt @@ -0,0 +1,30 @@ +# +# 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. +# + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testConfigure.h.cmake + ${CMAKE_CURRENT_BINARY_DIR}/testConfigure.h) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +corrade_add_test(TextAbstractFontTest AbstractFontTest.cpp LIBRARIES Magnum MagnumText) diff --git a/src/Text/Test/data.bin b/src/Text/Test/data.bin new file mode 100644 index 000000000..dd6737691 --- /dev/null +++ b/src/Text/Test/data.bin @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Text/Test/testConfigure.h.cmake b/src/Text/Test/testConfigure.h.cmake new file mode 100644 index 000000000..0cfeafcbe --- /dev/null +++ b/src/Text/Test/testConfigure.h.cmake @@ -0,0 +1,25 @@ +/* + 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. +*/ + +#define TEXT_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}" From 4faa67a19aa91ec93cda00e2728ec9fcde2d4c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 23 Jun 2013 15:56:03 +0200 Subject: [PATCH 05/36] Use forward declaration header instead. --- src/Magnum.h | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/Magnum.h b/src/Magnum.h index f6a867746..e1e0e58e6 100644 --- a/src/Magnum.h +++ b/src/Magnum.h @@ -28,7 +28,7 @@ * @brief Forward declarations for Magnum namespace */ -#include +#include #include "Math/Math.h" #include "Types.h" @@ -38,14 +38,6 @@ typedef unsigned int GLenum; /* Needed for *Format and *Type enums */ #endif -namespace Corrade { - namespace Utility { - class Debug; - class Warning; - class Error; - } -} - namespace Magnum { /** @todoc Remove `ifndef` when Doxygen is sane again */ From b23b0a46555e3db1a4c162456b03250c66014e10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 24 Jun 2013 18:59:57 +0200 Subject: [PATCH 06/36] Don't hide publicly used functions. I desperately need tests for GL functionality. --- src/AbstractTexture.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index 87179e638..de44bd57a 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -268,8 +268,8 @@ class MAGNUM_EXPORT AbstractTexture { void MAGNUM_LOCAL bindInternal(); #ifndef MAGNUM_TARGET_GLES - template void MAGNUM_LOCAL image(GLenum target, GLint level, Image* image); - template void MAGNUM_LOCAL image(GLenum target, GLint level, BufferImage* image, Buffer::Usage usage); + template void image(GLenum target, GLint level, Image* image); + template void image(GLenum target, GLint level, BufferImage* image, Buffer::Usage usage); #endif GLenum _target; From 5a368c4fbadbe03ba602650f97c0a5fbda332058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 24 Jun 2013 18:55:41 +0200 Subject: [PATCH 07/36] Math: ability to read and write Rectangle to configuration. --- src/CMakeLists.txt | 3 +- src/Math/Geometry/Rectangle.h | 30 +++++++++++++++++++ src/Math/Geometry/Test/RectangleTest.cpp | 16 +++++++++- src/Math/Geometry/instantiation.cpp | 38 ++++++++++++++++++++++++ 4 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 src/Math/Geometry/instantiation.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a204f4733..029e0e754 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -149,7 +149,8 @@ endif() # Files shared between main library and math unit test library set(MagnumMath_SRCS Math/Functions.cpp - Math/instantiation.cpp) + Math/instantiation.cpp + Math/Geometry/instantiation.cpp) # Set shared library flags for the objects, as they will be part of shared lib # TODO: fix when CMake sets target_EXPORTS for OBJECT targets as well diff --git a/src/Math/Geometry/Rectangle.h b/src/Math/Geometry/Rectangle.h index ad8ef7b20..41c3a4efc 100644 --- a/src/Math/Geometry/Rectangle.h +++ b/src/Math/Geometry/Rectangle.h @@ -148,4 +148,34 @@ template Corrade::Utility::Debug operator<<(Corrade::Utility::Debug deb }}} +namespace Corrade { namespace Utility { + +/** @configurationvalue{Magnum::Math::Geometry::Rectangle} */ +template struct ConfigurationValue> { + ConfigurationValue() = delete; + + /** @brief Writes elements separated with spaces */ + static std::string toString(const Magnum::Math::Geometry::Rectangle& value, const ConfigurationValueFlags flags) { + return ConfigurationValue>::toString( + reinterpret_cast&>(value), flags); + } + + /** @brief Reads elements separated with whitespace */ + static Magnum::Math::Geometry::Rectangle fromString(const std::string& stringValue, const ConfigurationValueFlags flags) { + const auto vec = ConfigurationValue>::fromString(stringValue, flags); + return reinterpret_cast&>(vec); + } +}; + +#ifndef DOXYGEN_GENERATING_OUTPUT +extern template struct MAGNUM_EXPORT ConfigurationValue>; +extern template struct MAGNUM_EXPORT ConfigurationValue>; +extern template struct MAGNUM_EXPORT ConfigurationValue>; +#ifndef MAGNUM_TARGET_GLES +extern template struct MAGNUM_EXPORT ConfigurationValue>; +#endif +#endif + +}} + #endif diff --git a/src/Math/Geometry/Test/RectangleTest.cpp b/src/Math/Geometry/Test/RectangleTest.cpp index 051f97670..b7d10facb 100644 --- a/src/Math/Geometry/Test/RectangleTest.cpp +++ b/src/Math/Geometry/Test/RectangleTest.cpp @@ -24,6 +24,7 @@ #include #include +#include #include "Math/Geometry/Rectangle.h" @@ -44,6 +45,7 @@ class RectangleTest: public Corrade::TestSuite::Tester { void size(); void debug(); + void configuration(); }; typedef Geometry::Rectangle Rectangle; @@ -61,7 +63,8 @@ RectangleTest::RectangleTest() { &RectangleTest::compare, &RectangleTest::size, - &RectangleTest::debug}); + &RectangleTest::debug, + &RectangleTest::configuration}); } void RectangleTest::construct() { @@ -148,6 +151,17 @@ void RectangleTest::debug() { CORRADE_COMPARE(o.str(), "Rectangle({34, 23}, {47, 30})\n"); } +void RectangleTest::configuration() { + Corrade::Utility::Configuration c; + + Rectangle rect({3.0f, 3.125f}, {9.0f, 9.55f}); + std::string value("3 3.125 9 9.55"); + + c.setValue("rectangle", rect); + CORRADE_COMPARE(c.value("rectangle"), value); + CORRADE_COMPARE(c.value("rectangle"), rect); +} + }}}} CORRADE_TEST_MAIN(Magnum::Math::Geometry::Test::RectangleTest) diff --git a/src/Math/Geometry/instantiation.cpp b/src/Math/Geometry/instantiation.cpp new file mode 100644 index 000000000..c483599aa --- /dev/null +++ b/src/Math/Geometry/instantiation.cpp @@ -0,0 +1,38 @@ +/* + 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 "Math/Geometry/Rectangle.h" + +namespace Corrade { namespace Utility { + +#ifndef DOXYGEN_GENERATING_OUTPUT +template struct ConfigurationValue>; +template struct ConfigurationValue>; +template struct ConfigurationValue>; +#ifndef MAGNUM_TARGET_GLES +template struct ConfigurationValue>; +#endif +#endif + +}} From 8e7156999ee09fec6b21b1a55dc37d3ac326c095 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 29 Jun 2013 01:53:25 +0200 Subject: [PATCH 08/36] Allow copying of ImageWrapper, movement of Image and Trade::ImageData. --- src/AbstractImage.cpp | 2 - src/AbstractImage.h | 17 +++----- src/Image.h | 24 +++++++++++ src/ImageWrapper.h | 9 ++-- src/Test/CMakeLists.txt | 1 + src/Test/ImageTest.cpp | 72 ++++++++++++++++++++++++++++++++ src/Trade/ImageData.h | 24 +++++++++++ src/Trade/Test/CMakeLists.txt | 1 + src/Trade/Test/ImageDataTest.cpp | 72 ++++++++++++++++++++++++++++++++ 9 files changed, 205 insertions(+), 17 deletions(-) create mode 100644 src/Test/ImageTest.cpp create mode 100644 src/Trade/Test/ImageDataTest.cpp diff --git a/src/AbstractImage.cpp b/src/AbstractImage.cpp index 0e3af2904..fa5ddcec5 100644 --- a/src/AbstractImage.cpp +++ b/src/AbstractImage.cpp @@ -30,8 +30,6 @@ namespace Magnum { -AbstractImage::~AbstractImage() {} - std::size_t AbstractImage::pixelSize(ImageFormat format, ImageType type) { std::size_t size = 0; switch(type) { diff --git a/src/AbstractImage.h b/src/AbstractImage.h index aef3c71ec..304c3613c 100644 --- a/src/AbstractImage.h +++ b/src/AbstractImage.h @@ -46,11 +46,6 @@ functions operating with images). It also possibly needs to be "stackable" to easily revert the state back. */ class MAGNUM_EXPORT AbstractImage { - AbstractImage(const AbstractImage&) = delete; - AbstractImage(AbstractImage&&) = delete; - AbstractImage& operator=(const AbstractImage&) = delete; - AbstractImage& operator=(AbstractImage&&) = delete; - public: /** * @brief Pixel size (in bytes) @@ -66,16 +61,13 @@ class MAGNUM_EXPORT AbstractImage { * @param format Format of pixel data * @param type Data type of pixel data */ - explicit AbstractImage(ImageFormat format, ImageType type): _format(format), _type(type) {} - - /** @brief Destructor */ - virtual ~AbstractImage() = 0; + constexpr explicit AbstractImage(ImageFormat format, ImageType type): _format(format), _type(type) {} /** @brief Format of pixel data */ - ImageFormat format() const { return _format; } + constexpr ImageFormat format() const { return _format; } /** @brief Data type of pixel data */ - ImageType type() const { return _type; } + constexpr ImageType type() const { return _type; } /** * @brief Pixel size (in bytes) @@ -84,6 +76,9 @@ class MAGNUM_EXPORT AbstractImage { */ std::size_t pixelSize() const { return pixelSize(_format, _type); } + protected: + ~AbstractImage() = default; + #ifdef DOXYGEN_GENERATING_OUTPUT private: #else diff --git a/src/Image.h b/src/Image.h index 54712ae18..6f854adce 100644 --- a/src/Image.h +++ b/src/Image.h @@ -67,6 +67,18 @@ template class Image: public AbstractImage { */ explicit Image(ImageFormat format, ImageType type): AbstractImage(format, type), _data(nullptr) {} + /** @brief Copying is not allowed */ + Image(const Image&& other) = delete; + + /** @brief Move constructor */ + Image(Image&& other) noexcept; + + /** @brief Copying is not allowed */ + Image& operator=(const Image&& other) = delete; + + /** @brief Move assignment */ + Image& operator=(Image&& other) noexcept; + /** @brief Destructor */ ~Image() { delete[] _data; } @@ -103,6 +115,18 @@ typedef Image<2> Image2D; /** @brief Three-dimensional image */ typedef Image<3> Image3D; +template inline Image::Image(Image&& other) noexcept: AbstractImage(std::move(other)), _size(std::move(other._size)), _data(std::move(other._data)) { + other._size = {}; + other._data = nullptr; +} + +template inline Image& Image::operator=(Image&& other) noexcept { + AbstractImage::operator=(std::move(other)); + std::swap(_size, other._size); + std::swap(_data, other._data); + return *this; +} + } #endif diff --git a/src/ImageWrapper.h b/src/ImageWrapper.h index 8acbd16ac..0050a2d63 100644 --- a/src/ImageWrapper.h +++ b/src/ImageWrapper.h @@ -52,6 +52,7 @@ Interchangeable with Image, BufferImage or Trade::ImageData. template class ImageWrapper: public AbstractImage { public: const static UnsignedInt Dimensions = dimensions; /**< @brief %Image dimension count */ + /** * @brief Constructor * @param size %Image size @@ -62,7 +63,7 @@ template class ImageWrapper: public AbstractImage { * Note that the image data are not copied on construction, but they * are deleted on class destruction. */ - explicit ImageWrapper(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} + constexpr explicit ImageWrapper(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} /** * @brief Constructor @@ -73,14 +74,14 @@ template class ImageWrapper: public AbstractImage { * Data pointer is set to zero, call setData() to fill the image with * data. */ - explicit ImageWrapper(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type): AbstractImage(format, type), _size(size), _data(nullptr) {} + constexpr explicit ImageWrapper(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type): AbstractImage(format, type), _size(size), _data(nullptr) {} /** @brief %Image size */ - typename DimensionTraits::VectorType size() const { return _size; } + constexpr typename DimensionTraits::VectorType size() const { return _size; } /** @brief Pointer to raw data */ unsigned char* data() { return _data; } - const unsigned char* data() const { return _data; } /**< @overload */ + constexpr const unsigned char* data() const { return _data; } /**< @overload */ /** * @brief Set image data diff --git a/src/Test/CMakeLists.txt b/src/Test/CMakeLists.txt index e9138989f..3998a58ee 100644 --- a/src/Test/CMakeLists.txt +++ b/src/Test/CMakeLists.txt @@ -28,6 +28,7 @@ corrade_add_test(ArrayTest ArrayTest.cpp) corrade_add_test(ColorTest ColorTest.cpp LIBRARIES MagnumMathTestLib) corrade_add_test(DefaultFramebufferTest DefaultFramebufferTest.cpp LIBRARIES Magnum) corrade_add_test(FramebufferTest FramebufferTest.cpp LIBRARIES Magnum) +corrade_add_test(ImageTest ImageTest.cpp LIBRARIES Magnum) corrade_add_test(MeshTest MeshTest.cpp LIBRARIES Magnum) corrade_add_test(RendererTest RendererTest.cpp LIBRARIES Magnum) corrade_add_test(ResourceManagerTest ResourceManagerTest.cpp LIBRARIES MagnumTestLib) diff --git a/src/Test/ImageTest.cpp b/src/Test/ImageTest.cpp new file mode 100644 index 000000000..70097e1f3 --- /dev/null +++ b/src/Test/ImageTest.cpp @@ -0,0 +1,72 @@ +/* + 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 "Image.h" +#include "ImageFormat.h" + +namespace Magnum { namespace Test { + +class ImageTest: public TestSuite::Tester { + public: + explicit ImageTest(); + + void moveConstructor(); + void moveAssignment(); +}; + +ImageTest::ImageTest() { + addTests({&ImageTest::moveConstructor, + &ImageTest::moveAssignment}); +} + +void ImageTest::moveConstructor() { + unsigned char* data = new unsigned char[3]; + Image2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + + Image2D b(std::move(a)); + CORRADE_VERIFY(!a.data()); + CORRADE_COMPARE(b.format(), ImageFormat::Red); + CORRADE_COMPARE(b.type(), ImageType::UnsignedByte); + CORRADE_COMPARE(b.size(), Vector2i(1, 3)); + CORRADE_VERIFY(b.data() == data); +} + +void ImageTest::moveAssignment() { + unsigned char* data = new unsigned char[3]; + Image2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + + Image2D b(ImageFormat::Red, ImageType::UnsignedByte); + b = std::move(a); + CORRADE_VERIFY(!a.data()); + CORRADE_COMPARE(b.format(), ImageFormat::Red); + CORRADE_COMPARE(b.type(), ImageType::UnsignedByte); + CORRADE_COMPARE(b.size(), Vector2i(1, 3)); + CORRADE_VERIFY(b.data() == data); +} + +}} + +CORRADE_TEST_MAIN(Magnum::Test::ImageTest) diff --git a/src/Trade/ImageData.h b/src/Trade/ImageData.h index ab134a96a..7e56c1359 100644 --- a/src/Trade/ImageData.h +++ b/src/Trade/ImageData.h @@ -57,6 +57,18 @@ template class ImageData: public AbstractImage { */ explicit ImageData(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} + /** @brief Copying is not allowed */ + ImageData(const ImageData&& other) = delete; + + /** @brief Move constructor */ + ImageData(ImageData&& other) noexcept; + + /** @brief Copying is not allowed */ + ImageData& operator=(const ImageData&& other) = delete; + + /** @brief Move assignment */ + ImageData& operator=(ImageData&& other) noexcept; + /** @brief Destructor */ ~ImageData() { delete[] _data; } @@ -81,6 +93,18 @@ typedef ImageData<2> ImageData2D; /** @brief Three-dimensional image */ typedef ImageData<3> ImageData3D; +template inline ImageData::ImageData(ImageData&& other) noexcept: AbstractImage(std::move(other)), _size(std::move(other._size)), _data(std::move(other._data)) { + other._size = {}; + other._data = nullptr; +} + +template inline ImageData& ImageData::operator=(ImageData&& other) noexcept { + AbstractImage::operator=(std::move(other)); + std::swap(_size, other._size); + std::swap(_data, other._data); + return *this; +} + }} #endif diff --git a/src/Trade/Test/CMakeLists.txt b/src/Trade/Test/CMakeLists.txt index a3c163349..beb1c2ccf 100644 --- a/src/Trade/Test/CMakeLists.txt +++ b/src/Trade/Test/CMakeLists.txt @@ -29,5 +29,6 @@ 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(TradeImageDataTest ImageDataTest.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/ImageDataTest.cpp b/src/Trade/Test/ImageDataTest.cpp new file mode 100644 index 000000000..6a5c300dc --- /dev/null +++ b/src/Trade/Test/ImageDataTest.cpp @@ -0,0 +1,72 @@ +/* + 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 "ImageFormat.h" +#include "Trade/ImageData.h" + +namespace Magnum { namespace Trade { namespace Test { + +class ImageDataTest: public TestSuite::Tester { + public: + explicit ImageDataTest(); + + void moveConstructor(); + void moveAssignment(); +}; + +ImageDataTest::ImageDataTest() { + addTests({&ImageDataTest::moveConstructor, + &ImageDataTest::moveAssignment}); +} + +void ImageDataTest::moveConstructor() { + unsigned char* data = new unsigned char[3]; + Trade::ImageData2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + + Trade::ImageData2D b(std::move(a)); + CORRADE_VERIFY(!a.data()); + CORRADE_COMPARE(b.format(), ImageFormat::Red); + CORRADE_COMPARE(b.type(), ImageType::UnsignedByte); + CORRADE_COMPARE(b.size(), Vector2i(1, 3)); + CORRADE_VERIFY(b.data() == data); +} + +void ImageDataTest::moveAssignment() { + unsigned char* data = new unsigned char[3]; + Trade::ImageData2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + + Trade::ImageData2D b({}, ImageFormat::Red, ImageType::UnsignedByte, nullptr); + b = std::move(a); + CORRADE_VERIFY(!a.data()); + CORRADE_COMPARE(b.format(), ImageFormat::Red); + CORRADE_COMPARE(b.type(), ImageType::UnsignedByte); + CORRADE_COMPARE(b.size(), Vector2i(1, 3)); + CORRADE_VERIFY(b.data() == data); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::ImageDataTest) From cecc1dc2bc885d5705e2d37ec2fcd5792dad82de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 29 Jun 2013 01:55:31 +0200 Subject: [PATCH 09/36] Renamed ImageWrapper to ImageReference. Better reflect what the class is actually about. --- src/AbstractImage.h | 2 +- src/BufferImage.h | 2 +- src/CMakeLists.txt | 2 +- src/CubeMapTexture.h | 4 ++-- src/CubeMapTextureArray.h | 6 +++--- src/Image.h | 2 +- src/ImageFormat.h | 4 ++-- src/{ImageWrapper.h => ImageReference.h} | 22 +++++++++++----------- src/Magnum.h | 8 ++++---- src/Texture.h | 4 ++-- src/Trade/ImageData.h | 2 +- 11 files changed, 29 insertions(+), 29 deletions(-) rename src/{ImageWrapper.h => ImageReference.h} (80%) diff --git a/src/AbstractImage.h b/src/AbstractImage.h index 304c3613c..412ee4179 100644 --- a/src/AbstractImage.h +++ b/src/AbstractImage.h @@ -38,7 +38,7 @@ namespace Magnum { /** @brief Non-templated base for one-, two- or three-dimensional images -See Image, ImageWrapper, BufferImage, Trade::ImageData documentation for +See Image, ImageReference, BufferImage, Trade::ImageData documentation for more information. @todo Where to put glClampColor() and glPixelStore() encapsulation? It is needed in AbstractFramebuffer::read(), Texture::setImage() etc (i.e. all diff --git a/src/BufferImage.h b/src/BufferImage.h index d49462817..24dfc3a6e 100644 --- a/src/BufferImage.h +++ b/src/BufferImage.h @@ -41,7 +41,7 @@ namespace Magnum { /** @brief %Buffer image -Stores image data in GPU memory. Interchangeable with Image, ImageWrapper or +Stores image data in GPU memory. Interchangeable with Image, ImageReference or Trade::ImageData. @see BufferImage1D, BufferImage2D, BufferImage3D, Buffer @requires_gles30 Pixel buffer objects are not available in OpenGL ES 2.0. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 029e0e754..60ce0ab96 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -113,7 +113,7 @@ set(Magnum_HEADERS Framebuffer.h Image.h ImageFormat.h - ImageWrapper.h + ImageReference.h Magnum.h Mesh.h OpenGL.h diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index b9f99ba88..724b71088 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -165,7 +165,7 @@ class CubeMapTexture: public AbstractTexture { * @param coordinate Coordinate * @param level Mip level * @param internalFormat Internal format - * @param image Image, ImageWrapper, BufferImage or + * @param image Image, ImageReference, BufferImage or * Trade::ImageData of the same dimension count * @return Pointer to self (for method chaining) * @@ -181,7 +181,7 @@ class CubeMapTexture: public AbstractTexture { * @param coordinate Coordinate * @param level Mip level * @param offset Offset where to put data in the texture - * @param image Image, ImageWrapper, BufferImage or + * @param image Image, ImageReference, BufferImage or * Trade::ImageData of the same or one less dimension count * @return Pointer to self (for method chaining) * diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index e4e443a2d..4d0676888 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -165,7 +165,7 @@ class CubeMapTextureArray: public AbstractTexture { * @brief Set image data * @param level Mip level * @param internalFormat Internal format - * @param image Image, ImageWrapper, BufferImage or + * @param image Image, ImageReference, BufferImage or * Trade::ImageData of the same dimension count * @return Pointer to self (for method chaining) * @@ -184,7 +184,7 @@ class CubeMapTextureArray: public AbstractTexture { * @brief Set texture image 3D subdata * @param level Mip level * @param offset Offset where to put data in the texture - * @param image Image3D, ImageWrapper3D, BufferImage3D or + * @param image Image3D, ImageReference3D, BufferImage3D or * Trade::ImageData3D * @return Pointer to self (for method chaining) * @@ -210,7 +210,7 @@ class CubeMapTextureArray: public AbstractTexture { * @param coordinate Coordinate * @param level Mip level * @param offset Offset where to put data in the texture - * @param image Image2D, ImageWrapper2D, BufferImage2D or + * @param image Image2D, ImageReference2D, BufferImage2D or * Trade::ImageData2D * @return Pointer to self (for method chaining) * diff --git a/src/Image.h b/src/Image.h index 6f854adce..42efbb6ee 100644 --- a/src/Image.h +++ b/src/Image.h @@ -37,7 +37,7 @@ namespace Magnum { /** @brief %Image -Stores image data on client memory. Interchangeable with ImageWrapper, +Stores image data on client memory. Interchangeable with ImageReference, BufferImage or Trade::ImageData. @see Image1D, Image2D, Image3D */ diff --git a/src/ImageFormat.h b/src/ImageFormat.h index 2a358eca7..308f1f023 100644 --- a/src/ImageFormat.h +++ b/src/ImageFormat.h @@ -40,7 +40,7 @@ namespace Magnum { Note that some formats can be used only for framebuffer reading (using AbstractFramebuffer::read()) and some only for texture data (using Texture::setImage() and others). -@see Image, ImageWrapper, BufferImage, Trade::ImageData +@see Image, ImageReference, BufferImage, Trade::ImageData */ enum class ImageFormat: GLenum { /** @@ -254,7 +254,7 @@ enum class ImageFormat: GLenum { Note that some formats can be used only for framebuffer reading (using AbstractFramebuffer::read()) and some only for texture data (using Texture::setImage() and others). -@see Image, ImageWrapper, BufferImage, Trade::ImageData +@see Image, ImageReference, BufferImage, Trade::ImageData */ enum class ImageType: GLenum { /** Each component unsigned byte. */ diff --git a/src/ImageWrapper.h b/src/ImageReference.h similarity index 80% rename from src/ImageWrapper.h rename to src/ImageReference.h index 0050a2d63..f42498793 100644 --- a/src/ImageWrapper.h +++ b/src/ImageReference.h @@ -1,5 +1,5 @@ -#ifndef Magnum_ImageWrapper_h -#define Magnum_ImageWrapper_h +#ifndef Magnum_ImageReference_h +#define Magnum_ImageReference_h /* This file is part of Magnum. @@ -25,7 +25,7 @@ */ /** @file - * @brief Class Magnum::ImageWrapper + * @brief Class Magnum::ImageReference */ #include "Math/Vector3.h" @@ -35,7 +35,7 @@ namespace Magnum { /** -@brief %Image wrapper +@brief %Image reference Adds information about dimensions, color components and component type to some data in memory. @@ -47,9 +47,9 @@ same properties for each frame, such as video stream. Thus it is not possible to change image properties, only data pointer. Interchangeable with Image, BufferImage or Trade::ImageData. -@see ImageWrapper1D, ImageWrapper2D, ImageWrapper3D +@see ImageReference1D, ImageReference2D, ImageReference3D */ -template class ImageWrapper: public AbstractImage { +template class ImageReference: public AbstractImage { public: const static UnsignedInt Dimensions = dimensions; /**< @brief %Image dimension count */ @@ -63,7 +63,7 @@ template class ImageWrapper: public AbstractImage { * Note that the image data are not copied on construction, but they * are deleted on class destruction. */ - constexpr explicit ImageWrapper(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} + constexpr explicit ImageReference(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} /** * @brief Constructor @@ -74,7 +74,7 @@ template class ImageWrapper: public AbstractImage { * Data pointer is set to zero, call setData() to fill the image with * data. */ - constexpr explicit ImageWrapper(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type): AbstractImage(format, type), _size(size), _data(nullptr) {} + constexpr explicit ImageReference(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type): AbstractImage(format, type), _size(size), _data(nullptr) {} /** @brief %Image size */ constexpr typename DimensionTraits::VectorType size() const { return _size; } @@ -101,13 +101,13 @@ template class ImageWrapper: public AbstractImage { }; /** @brief One-dimensional image wrapper */ -typedef ImageWrapper<1> ImageWrapper1D; +typedef ImageReference<1> ImageReference1D; /** @brief Two-dimensional image wrapper */ -typedef ImageWrapper<2> ImageWrapper2D; +typedef ImageReference<2> ImageReference2D; /** @brief Three-dimensional image wrapper */ -typedef ImageWrapper<3> ImageWrapper3D; +typedef ImageReference<3> ImageReference3D; } diff --git a/src/Magnum.h b/src/Magnum.h index e1e0e58e6..53475bb91 100644 --- a/src/Magnum.h +++ b/src/Magnum.h @@ -378,10 +378,10 @@ typedef Image<3> Image3D; enum class ImageFormat: GLenum; enum class ImageType: GLenum; -template class ImageWrapper; -typedef ImageWrapper<1> ImageWrapper1D; -typedef ImageWrapper<2> ImageWrapper2D; -typedef ImageWrapper<3> ImageWrapper3D; +template class ImageReference; +typedef ImageReference<1> ImageReference1D; +typedef ImageReference<2> ImageReference2D; +typedef ImageReference<3> ImageReference3D; class Mesh; diff --git a/src/Texture.h b/src/Texture.h index 9cd269e5f..8dd306f2f 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -346,7 +346,7 @@ template class Texture: public AbstractTexture { * @brief Set image data * @param level Mip level * @param internalFormat Internal format - * @param image Image, ImageWrapper, BufferImage or + * @param image Image, ImageReference, BufferImage or * Trade::ImageData of the same dimension count * @return Pointer to self (for method chaining) * @@ -373,7 +373,7 @@ template class Texture: public AbstractTexture { * @brief Set image subdata * @param level Mip level * @param offset Offset where to put data in the texture - * @param image Image, ImageWrapper, BufferImage or + * @param image Image, ImageReference, BufferImage or * Trade::ImageData of the same or one less dimension count * @return Pointer to self (for method chaining) * diff --git a/src/Trade/ImageData.h b/src/Trade/ImageData.h index 7e56c1359..f0eec7c30 100644 --- a/src/Trade/ImageData.h +++ b/src/Trade/ImageData.h @@ -38,7 +38,7 @@ namespace Magnum { namespace Trade { @brief %Image data Access to image data provided by AbstractImporter subclasses. Interchangeable -with Image, ImageWrapper or BufferImage. +with Image, ImageReference or BufferImage. @see ImageData1D, ImageData2D, ImageData3D */ template class ImageData: public AbstractImage { From fbdd132b71e580e63714050044cb8058cec368a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 30 Jun 2013 19:44:28 +0200 Subject: [PATCH 10/36] Allow conversion of Image and Trade::ImageData to ImageReference. --- src/Image.h | 15 ++++++++++++--- src/Test/ImageTest.cpp | 15 ++++++++++++++- src/Trade/ImageData.h | 15 ++++++++++++--- src/Trade/Test/ImageDataTest.cpp | 15 ++++++++++++++- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/Image.h b/src/Image.h index 42efbb6ee..cc06f228e 100644 --- a/src/Image.h +++ b/src/Image.h @@ -28,9 +28,7 @@ * @brief Class Magnum::Image, typedef Magnum::Image1D, Magnum::Image2D, Magnum::Image3D */ -#include "Math/Vector3.h" -#include "AbstractImage.h" -#include "DimensionTraits.h" +#include "ImageReference.h" namespace Magnum { @@ -82,6 +80,13 @@ template class Image: public AbstractImage { /** @brief Destructor */ ~Image() { delete[] _data; } + /** + * @brief Conversion to reference + * + * @todo GCC 4.8: don't allow this on rvalue-ref + */ + /*implicit*/ operator ImageReference() const; + /** @brief %Image size */ typename DimensionTraits::VectorType size() const { return _size; } @@ -127,6 +132,10 @@ template inline Image& Image::op return *this; } +template inline Image::operator ImageReference() const { + return ImageReference(_size, this->format(), this->type(), _data); +} + } #endif diff --git a/src/Test/ImageTest.cpp b/src/Test/ImageTest.cpp index 70097e1f3..fbf690f7c 100644 --- a/src/Test/ImageTest.cpp +++ b/src/Test/ImageTest.cpp @@ -35,11 +35,13 @@ class ImageTest: public TestSuite::Tester { void moveConstructor(); void moveAssignment(); + void toReference(); }; ImageTest::ImageTest() { addTests({&ImageTest::moveConstructor, - &ImageTest::moveAssignment}); + &ImageTest::moveAssignment, + &ImageTest::toReference}); } void ImageTest::moveConstructor() { @@ -67,6 +69,17 @@ void ImageTest::moveAssignment() { CORRADE_VERIFY(b.data() == data); } +void ImageTest::toReference() { + unsigned char* data = new unsigned char[3]; + Image2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + + ImageReference2D b = a; + CORRADE_COMPARE(b.format(), ImageFormat::Red); + CORRADE_COMPARE(b.type(), ImageType::UnsignedByte); + CORRADE_COMPARE(b.size(), Vector2i(1, 3)); + CORRADE_VERIFY(b.data() == data); +} + }} CORRADE_TEST_MAIN(Magnum::Test::ImageTest) diff --git a/src/Trade/ImageData.h b/src/Trade/ImageData.h index f0eec7c30..dd1e2f2a3 100644 --- a/src/Trade/ImageData.h +++ b/src/Trade/ImageData.h @@ -28,9 +28,7 @@ * @brief Class Magnum::Trade::ImageData */ -#include "Math/Vector3.h" -#include "AbstractImage.h" -#include "DimensionTraits.h" +#include "ImageReference.h" namespace Magnum { namespace Trade { @@ -72,6 +70,13 @@ template class ImageData: public AbstractImage { /** @brief Destructor */ ~ImageData() { delete[] _data; } + /** + * @brief Conversion to reference + * + * @todo GCC 4.8: don't allow this on rvalue-ref + */ + /*implicit*/ operator ImageReference() const; + /** @brief %Image size */ typename DimensionTraits::VectorType size() const { return _size; } @@ -105,6 +110,10 @@ template inline ImageData& ImageData inline ImageData::operator ImageReference() const { + return ImageReference(_size, this->format(), this->type(), _data); +} + }} #endif diff --git a/src/Trade/Test/ImageDataTest.cpp b/src/Trade/Test/ImageDataTest.cpp index 6a5c300dc..b2d428a9e 100644 --- a/src/Trade/Test/ImageDataTest.cpp +++ b/src/Trade/Test/ImageDataTest.cpp @@ -35,11 +35,13 @@ class ImageDataTest: public TestSuite::Tester { void moveConstructor(); void moveAssignment(); + void toReference(); }; ImageDataTest::ImageDataTest() { addTests({&ImageDataTest::moveConstructor, - &ImageDataTest::moveAssignment}); + &ImageDataTest::moveAssignment, + &ImageDataTest::toReference}); } void ImageDataTest::moveConstructor() { @@ -67,6 +69,17 @@ void ImageDataTest::moveAssignment() { CORRADE_VERIFY(b.data() == data); } +void ImageDataTest::toReference() { + unsigned char* data = new unsigned char[3]; + Trade::ImageData2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + + ImageReference2D b = a; + CORRADE_COMPARE(b.format(), ImageFormat::Red); + CORRADE_COMPARE(b.type(), ImageType::UnsignedByte); + CORRADE_COMPARE(b.size(), Vector2i(1, 3)); + CORRADE_COMPARE(b.data(), data); +} + }}} CORRADE_TEST_MAIN(Magnum::Trade::Test::ImageDataTest) From dc2ab6ca472ed5ff98d2b3dfd0a897ff99fd5db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 01:22:35 +0200 Subject: [PATCH 11/36] Document disallowed copy and allowed move better. Moreover when the deleted function was private, the compilation failed with (rather cryptic) error that the function is private instead of noticing that the function is deleted. --- src/AbstractTexture.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index de44bd57a..6934a5f4c 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -95,9 +95,6 @@ do nothing. class MAGNUM_EXPORT AbstractTexture { friend class Context; - AbstractTexture(const AbstractTexture&) = delete; - AbstractTexture& operator=(const AbstractTexture&) = delete; - public: /** * @brief Max supported layer count @@ -124,9 +121,15 @@ class MAGNUM_EXPORT AbstractTexture { */ virtual ~AbstractTexture() = 0; + /** @brief Copying is not allowed */ + AbstractTexture(const AbstractTexture&) = delete; + /** @brief Move constructor */ AbstractTexture(AbstractTexture&& other); + /** @brief Copying is not allowed */ + AbstractTexture& operator=(const AbstractTexture&) = delete; + /** @brief Move assignment */ AbstractTexture& operator=(AbstractTexture&& other); From 87fff877bab028714698a404118371e04abdf4a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 01:28:22 +0200 Subject: [PATCH 12/36] Pass image as reference, not pointer to *Texture::image(). Avoids some nullptr-related errors. --- src/AbstractTexture.cpp | 32 ++++++++++++++++---------------- src/AbstractTexture.h | 4 ++-- src/CubeMapTexture.h | 8 ++++---- src/CubeMapTextureArray.h | 8 ++++---- src/Texture.h | 6 +++--- 5 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/AbstractTexture.cpp b/src/AbstractTexture.cpp index e085764ae..70d443b5b 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -949,31 +949,31 @@ void AbstractTexture::invalidateSubImageImplementationARB(GLint level, const Vec #ifndef DOXYGEN_GENERATING_OUTPUT #ifndef MAGNUM_TARGET_GLES -template void AbstractTexture::image(GLenum target, GLint level, Image* image) { +template void AbstractTexture::image(GLenum target, GLint level, Image& image) { const Math::Vector size = DataHelper::imageSize(this, target, level); - const std::size_t dataSize = size.product()*image->pixelSize(); + const std::size_t dataSize = size.product()*image.pixelSize(); char* data = new char[dataSize]; - (this->*getImageImplementation)(target, level, image->format(), image->type(), dataSize, data); - image->setData(size, image->format(), image->type(), data); + (this->*getImageImplementation)(target, level, image.format(), image.type(), dataSize, data); + image.setData(size, image.format(), image.type(), data); } -template void AbstractTexture::image<1>(GLenum, GLint, Image<1>*); -template void AbstractTexture::image<2>(GLenum, GLint, Image<2>*); -template void AbstractTexture::image<3>(GLenum, GLint, Image<3>*); +template void AbstractTexture::image<1>(GLenum, GLint, Image<1>&); +template void AbstractTexture::image<2>(GLenum, GLint, Image<2>&); +template void AbstractTexture::image<3>(GLenum, GLint, Image<3>&); -template void AbstractTexture::image(GLenum target, GLint level, BufferImage* image, Buffer::Usage usage) { +template void AbstractTexture::image(GLenum target, GLint level, BufferImage& image, Buffer::Usage usage) { const Math::Vector size = DataHelper::imageSize(this, target, level); - const std::size_t dataSize = size.product()*image->pixelSize(); - if(image->size() != size) - image->setData(size, image->format(), image->type(), nullptr, usage); + const std::size_t dataSize = size.product()*image.pixelSize(); + if(image.size() != size) + image.setData(size, image.format(), image.type(), nullptr, usage); - image->buffer()->bind(Buffer::Target::PixelPack); - (this->*getImageImplementation)(target, level, image->format(), image->type(), dataSize, nullptr); + image.buffer()->bind(Buffer::Target::PixelPack); + (this->*getImageImplementation)(target, level, image.format(), image.type(), dataSize, nullptr); } -template void AbstractTexture::image<1>(GLenum, GLint, BufferImage<1>*, Buffer::Usage); -template void AbstractTexture::image<2>(GLenum, GLint, BufferImage<2>*, Buffer::Usage); -template void AbstractTexture::image<3>(GLenum, GLint, BufferImage<3>*, Buffer::Usage); +template void AbstractTexture::image<1>(GLenum, GLint, BufferImage<1>&, Buffer::Usage); +template void AbstractTexture::image<2>(GLenum, GLint, BufferImage<2>&, Buffer::Usage); +template void AbstractTexture::image<3>(GLenum, GLint, BufferImage<3>&, Buffer::Usage); #endif #endif diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index 6934a5f4c..83b0d3249 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -271,8 +271,8 @@ class MAGNUM_EXPORT AbstractTexture { void MAGNUM_LOCAL bindInternal(); #ifndef MAGNUM_TARGET_GLES - template void image(GLenum target, GLint level, Image* image); - template void image(GLenum target, GLint level, BufferImage* image, Buffer::Usage usage); + template void image(GLenum target, GLint level, Image& image); + template void image(GLenum target, GLint level, BufferImage& image, Buffer::Usage usage); #endif GLenum _target; diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index 724b71088..0bc8a642c 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -137,10 +137,10 @@ class CubeMapTexture: public AbstractTexture { * @param level Mip level * @param image %Image where to put the data * - * See Texture::image(Int, Image*) for more information. + * See Texture::image(Int, Image&) for more information. * @requires_gl %Texture image queries are not available in OpenGL ES. */ - void image(Coordinate coordinate, Int level, Image2D* image) { + void image(Coordinate coordinate, Int level, Image2D& image) { AbstractTexture::image<2>(GLenum(coordinate), level, image); } @@ -151,11 +151,11 @@ class CubeMapTexture: public AbstractTexture { * @param image %Buffer image where to put the data * @param usage %Buffer usage * - * See Texture::image(Int, BufferImage*, Buffer::Usage) for more + * See Texture::image(Int, BufferImage&, Buffer::Usage) for more * information. * @requires_gl %Texture image queries are not available in OpenGL ES. */ - void image(Coordinate coordinate, Int level, BufferImage2D* image, Buffer::Usage usage) { + void image(Coordinate coordinate, Int level, BufferImage2D& image, Buffer::Usage usage) { AbstractTexture::image<2>(GLenum(coordinate), level, image, usage); } #endif diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index 4d0676888..0f64d3720 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -138,10 +138,10 @@ class CubeMapTextureArray: public AbstractTexture { * @param level Mip level * @param image %Image where to put the data * - * See Texture::image(Int, Image*) for more information. + * See Texture::image(Int, Image&) for more information. * @requires_gl %Texture image queries are not available in OpenGL ES. */ - void image(Coordinate coordinate, Int level, Image3D* image) { + void image(Coordinate coordinate, Int level, Image3D& image) { AbstractTexture::image<3>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + GLenum(coordinate), level, image); } @@ -152,11 +152,11 @@ class CubeMapTextureArray: public AbstractTexture { * @param image %Buffer image where to put the data * @param usage %Buffer usage * - * See Texture::image(Int, BufferImage*, Buffer::Usage) for more + * See Texture::image(Int, BufferImage&, Buffer::Usage) for more * information. * @requires_gl %Texture image queries are not available in OpenGL ES. */ - void image(Coordinate coordinate, Int level, BufferImage3D* image, Buffer::Usage usage) { + void image(Coordinate coordinate, Int level, BufferImage3D& image, Buffer::Usage usage) { AbstractTexture::image<3>(GL_TEXTURE_CUBE_MAP_POSITIVE_X + GLenum(coordinate), level, image, usage); } #endif diff --git a/src/Texture.h b/src/Texture.h index 8dd306f2f..a6c6e0a68 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -324,7 +324,7 @@ template class Texture: public AbstractTexture { * then @fn_gl{GetTexImage}, @fn_gl_extension{GetTextureImage,EXT,direct_state_access} * or @fn_gl_extension{GetnTexImage,ARB,robustness} */ - void image(Int level, Image* image) { + void image(Int level, Image& image) { AbstractTexture::image(_target, level, image); } @@ -334,10 +334,10 @@ template class Texture: public AbstractTexture { * @param image %Buffer image where to put the data * @param usage %Buffer usage * - * See image(Int, Image*) for more information. + * See image(Int, Image&) for more information. * @requires_gl %Texture image queries are not available in OpenGL ES. */ - void image(Int level, BufferImage* image, Buffer::Usage usage) { + void image(Int level, BufferImage& image, Buffer::Usage usage) { AbstractTexture::image(_target, level, image, usage); } #endif From 6025946e5e822fa60e437fd3fdf15e7fb1a8eb4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 01:29:29 +0200 Subject: [PATCH 13/36] Pass image as reference, not pointer to *Framebuffer::read(). Avoids some nullptr-related errors. --- src/AbstractFramebuffer.cpp | 18 +++++++++--------- src/AbstractFramebuffer.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/AbstractFramebuffer.cpp b/src/AbstractFramebuffer.cpp index c410aac91..1097fb10a 100644 --- a/src/AbstractFramebuffer.cpp +++ b/src/AbstractFramebuffer.cpp @@ -141,20 +141,20 @@ void AbstractFramebuffer::clear(FramebufferClearMask mask) { glClear(static_cast(mask)); } -void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, Image2D* image) { +void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, Image2D& image) { #ifndef MAGNUM_TARGET_GLES2 bindInternal(FramebufferTarget::Read); #else bindInternal(readTarget); #endif - const std::size_t dataSize = image->pixelSize()*size.product(); + const std::size_t dataSize = image.pixelSize()*size.product(); char* const data = new char[dataSize]; - readImplementation(offset, size, image->format(), image->type(), dataSize, data); - image->setData(size, image->format(), image->type(), data); + readImplementation(offset, size, image.format(), image.type(), dataSize, data); + image.setData(size, image.format(), image.type(), data); } #ifndef MAGNUM_TARGET_GLES2 -void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, BufferImage2D* image, Buffer::Usage usage) { +void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, BufferImage2D& image, Buffer::Usage usage) { #ifndef MAGNUM_TARGET_GLES2 bindInternal(FramebufferTarget::Read); #else @@ -162,12 +162,12 @@ void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, Buf #endif /* If the buffer doesn't have sufficient size, resize it */ /** @todo Explicitly reset also when buffer usage changes */ - if(image->size() != size) - image->setData(size, image->format(), image->type(), nullptr, usage); + if(image.size() != size) + image.setData(size, image.format(), image.type(), nullptr, usage); - image->buffer()->bind(Buffer::Target::PixelPack); + image.buffer()->bind(Buffer::Target::PixelPack); /** @todo De-duplicate buffer size computation */ - readImplementation(offset, size, image->format(), image->type(), image->pixelSize()*size.product(), nullptr); + readImplementation(offset, size, image.format(), image.type(), image.pixelSize()*size.product(), nullptr); } #endif diff --git a/src/AbstractFramebuffer.h b/src/AbstractFramebuffer.h index 606ae8593..184e70ae3 100644 --- a/src/AbstractFramebuffer.h +++ b/src/AbstractFramebuffer.h @@ -246,7 +246,7 @@ class MAGNUM_EXPORT AbstractFramebuffer { * @see @fn_gl{BindFramebuffer}, @fn_gl{ReadPixels} or * @fn_gl_extension{ReadnPixels,ARB,robustness} */ - void read(const Vector2i& offset, const Vector2i& size, Image2D* image); + void read(const Vector2i& offset, const Vector2i& size, Image2D& image); #ifndef MAGNUM_TARGET_GLES2 /** @@ -260,7 +260,7 @@ class MAGNUM_EXPORT AbstractFramebuffer { * information. * @requires_gles30 Pixel buffer objects are not available in OpenGL ES 2.0. */ - void read(const Vector2i& offset, const Vector2i& size, BufferImage2D* image, Buffer::Usage usage); + void read(const Vector2i& offset, const Vector2i& size, BufferImage2D& image, Buffer::Usage usage); #endif #ifdef DOXYGEN_GENERATING_OUTPUT From a500be6ef2291ff3dd9fca27705132cb8ce91521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 01:31:36 +0200 Subject: [PATCH 14/36] Pass image as reference, not pointer to *Texture::set*Image(). Avoids some nullptr-related errors and leads to simpler implementation, as we need to only handle ImageReference and BufferImage types. All other Image classes are implicitly convertible to ImageReference and implicit conversion works only with references, not with pointers. The usage might be clumsy at this point, as Trade::*Importer classes return pointer to Trade::ImageData which then needs to be dereferenced. Hopefully introducing C++14's std::optional (or equivalent) might help solve that issue. Also removed "convenience" overloads for setting e.g. 2D subdata of 3D texture. The convenience overload doesn't cover all subdata cases (only XY plane, not YZ or XZ ones) and overly complicates the implementation. This can be done in the future in Image classes themselves. --- src/AbstractTexture.cpp | 91 ++++++++++++++++++++++++---- src/AbstractTexture.h | 65 +++++++------------- src/CubeMapTexture.h | 26 ++++++-- src/CubeMapTextureArray.h | 31 ++++------ src/Text/DistanceFieldGlyphCache.cpp | 20 +++--- src/Text/DistanceFieldGlyphCache.h | 4 +- src/Text/GlyphCache.cpp | 2 +- src/Text/GlyphCache.h | 2 +- src/Texture.h | 37 +++++------ 9 files changed, 165 insertions(+), 113 deletions(-) diff --git a/src/AbstractTexture.cpp b/src/AbstractTexture.cpp index 70d443b5b..22c898d27 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -978,19 +978,6 @@ template void AbstractTexture::image<3>(GLenum, GLint, BufferImage<3>&, Buffer:: #endif #ifndef DOXYGEN_GENERATING_OUTPUT -#ifndef MAGNUM_TARGET_GLES2 -namespace Implementation { - template const GLvoid* ImageHelper>::dataOrPixelUnpackBuffer(BufferImage* image) { - image->buffer()->bind(Buffer::Target::PixelUnpack); - return nullptr; - } - - template struct ImageHelper; - template struct ImageHelper; - template struct ImageHelper; -} -#endif - #ifndef MAGNUM_TARGET_GLES Math::Vector<1, GLint> AbstractTexture::DataHelper<1>::imageSize(AbstractTexture* texture, GLenum target, GLint level) { Math::Vector<1, GLint> value; @@ -1014,6 +1001,84 @@ Vector3i AbstractTexture::DataHelper<3>::imageSize(AbstractTexture* texture, GLe } #endif +#ifndef MAGNUM_TARGET_GLES +void AbstractTexture::DataHelper<1>::setImage(AbstractTexture* const texture, const GLenum target, const GLint level, const TextureFormat internalFormat, const ImageReference1D& image) { + Buffer::unbind(Buffer::Target::PixelUnpack); + (texture->*image1DImplementation)(target, level, internalFormat, image.size(), image.format(), image.type(), image.data()); +} + +void AbstractTexture::DataHelper<1>::setImage(AbstractTexture* const texture, const GLenum target, const GLint level, const TextureFormat internalFormat, BufferImage1D& image) { + image.buffer()->bind(Buffer::Target::PixelUnpack); + (texture->*image1DImplementation)(target, level, internalFormat, image.size(), image.format(), image.type(), nullptr); +} + +void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture* const texture, const GLenum target, const GLint level, const Math::Vector<1, GLint>& offset, const ImageReference1D& image) { + Buffer::unbind(Buffer::Target::PixelUnpack); + (texture->*subImage1DImplementation)(target, level, offset, image.size(), image.format(), image.type(), image.data()); +} + +void AbstractTexture::DataHelper<1>::setSubImage(AbstractTexture* const texture, const GLenum target, const GLint level, const Math::Vector<1, GLint>& offset, BufferImage1D& image) { + image.buffer()->bind(Buffer::Target::PixelUnpack); + (texture->*subImage1DImplementation)(target, level, offset, image.size(), image.format(), image.type(), nullptr); +} +#endif + +void AbstractTexture::DataHelper<2>::setImage(AbstractTexture* const texture, const GLenum target, const GLint level, const TextureFormat internalFormat, const ImageReference2D& image) { + #ifndef MAGNUM_TARGET_GLES2 + Buffer::unbind(Buffer::Target::PixelUnpack); + #endif + (texture->*image2DImplementation)(target, level, internalFormat, image.size(), image.format(), image.type(), image.data()); +} + +#ifndef MAGNUM_TARGET_GLES2 +void AbstractTexture::DataHelper<2>::setImage(AbstractTexture* const texture, const GLenum target, const GLint level, const TextureFormat internalFormat, BufferImage2D& image) { + image.buffer()->bind(Buffer::Target::PixelUnpack); + (texture->*image2DImplementation)(target, level, internalFormat, image.size(), image.format(), image.type(), nullptr); +} +#endif + +void AbstractTexture::DataHelper<2>::setSubImage(AbstractTexture* const texture, const GLenum target, const GLint level, const Vector2i& offset, const ImageReference2D& image) { + #ifndef MAGNUM_TARGET_GLES2 + Buffer::unbind(Buffer::Target::PixelUnpack); + #endif + (texture->*subImage2DImplementation)(target, level, offset, image.size(), image.format(), image.type(), image.data()); +} + +#ifndef MAGNUM_TARGET_GLES2 +void AbstractTexture::DataHelper<2>::setSubImage(AbstractTexture* const texture, const GLenum target, const GLint level, const Vector2i& offset, BufferImage2D& image) { + image.buffer()->bind(Buffer::Target::PixelUnpack); + (texture->*subImage2DImplementation)(target, level, offset, image.size(), image.format(), image.type(), nullptr); +} +#endif + +void AbstractTexture::DataHelper<3>::setImage(AbstractTexture* const texture, const GLenum target, const GLint level, const TextureFormat internalFormat, const ImageReference3D& image) { + #ifndef MAGNUM_TARGET_GLES2 + Buffer::unbind(Buffer::Target::PixelUnpack); + #endif + (texture->*image3DImplementation)(target, level, internalFormat, image.size(), image.format(), image.type(), image.data()); +} + +#ifndef MAGNUM_TARGET_GLES2 +void AbstractTexture::DataHelper<3>::setImage(AbstractTexture* const texture, const GLenum target, const GLint level, const TextureFormat internalFormat, BufferImage3D& image) { + image.buffer()->bind(Buffer::Target::PixelUnpack); + (texture->*image3DImplementation)(target, level, internalFormat, image.size(), image.format(), image.type(), nullptr); +} +#endif + +void AbstractTexture::DataHelper<3>::setSubImage(AbstractTexture* const texture, const GLenum target, const GLint level, const Vector3i& offset, const ImageReference3D& image) { + #ifndef MAGNUM_TARGET_GLES2 + Buffer::unbind(Buffer::Target::PixelUnpack); + #endif + (texture->*subImage3DImplementation)(target, level, offset, image.size(), image.format(), image.type(), image.data()); +} + +#ifndef MAGNUM_TARGET_GLES2 +void AbstractTexture::DataHelper<3>::setSubImage(AbstractTexture* const texture, const GLenum target, const GLint level, const Vector3i& offset, BufferImage3D& image) { + image.buffer()->bind(Buffer::Target::PixelUnpack); + (texture->*subImage3DImplementation)(target, level, offset, image.size(), image.format(), image.type(), nullptr); +} +#endif + void AbstractTexture::DataHelper<2>::setWrapping(AbstractTexture* texture, const Array2D& wrapping) { #ifndef MAGNUM_TARGET_GLES CORRADE_ASSERT(texture->_target != GL_TEXTURE_RECTANGLE || ((wrapping.x() == Sampler::Wrapping::ClampToEdge || wrapping.x() == Sampler::Wrapping::ClampToBorder) && (wrapping.y() == Sampler::Wrapping::ClampToEdge || wrapping.y() == Sampler::Wrapping::ClampToEdge)), "AbstractTexture: rectangle texture wrapping must either clamp to border or to edge", ); diff --git a/src/AbstractTexture.h b/src/AbstractTexture.h index 83b0d3249..0cf278646 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -419,25 +419,8 @@ class MAGNUM_EXPORT AbstractTexture { }; #ifndef DOXYGEN_GENERATING_OUTPUT -namespace Implementation { - template struct ImageHelper { - static const GLvoid* dataOrPixelUnpackBuffer(Image* image) { - #ifndef MAGNUM_TARGET_GLES2 - Buffer::unbind(Buffer::Target::PixelUnpack); - #endif - return image->data(); - } - }; - - #ifndef MAGNUM_TARGET_GLES2 - template struct MAGNUM_EXPORT ImageHelper> { - static const GLvoid* dataOrPixelUnpackBuffer(BufferImage* image); - }; - #endif -} - #ifndef MAGNUM_TARGET_GLES -template<> struct AbstractTexture::DataHelper<1> { +template<> struct MAGNUM_EXPORT AbstractTexture::DataHelper<1> { enum class Target: GLenum { Texture1D = GL_TEXTURE_1D }; @@ -454,13 +437,11 @@ template<> struct AbstractTexture::DataHelper<1> { (texture->*storage1DImplementation)(target, levels, internalFormat, size); } - template static typename std::enable_if::type setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, Image* image) { - (texture->*image1DImplementation)(target, level, internalFormat, image->size(), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } + static void setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, const ImageReference1D& image); + static void setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, BufferImage1D& image); - template static typename std::enable_if::type setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Math::Vector<1, GLint>& offset, Image* image) { - (texture->*subImage1DImplementation)(target, level, offset, image->size(), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } + static void setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Math::Vector<1, GLint>& offset, const ImageReference1D& image); + static void setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Math::Vector<1, GLint>& offset, BufferImage1D& image); static void invalidateSubImage(AbstractTexture* texture, GLint level, const Math::Vector<1, GLint>& offset, const Math::Vector<1, GLint>& size) { (texture->*invalidateSubImageImplementation)(level, {offset[0], 0, 0}, {size[0], 1, 1}); @@ -489,17 +470,15 @@ template<> struct MAGNUM_EXPORT AbstractTexture::DataHelper<2> { (texture->*storage2DImplementation)(target, levels, internalFormat, size); } - template static typename std::enable_if::type setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, Image* image) { - (texture->*image2DImplementation)(target, level, internalFormat, image->size(), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } - - template static typename std::enable_if::type setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector2i& offset, Image* image) { - (texture->*subImage2DImplementation)(target, level, offset, image->size(), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } + static void setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, const ImageReference2D& image); + #ifndef MAGNUM_TARGET_GLES2 + static void setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, BufferImage2D& image); + #endif - template static typename std::enable_if::type setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector2i& offset, Image* image) { - (texture->*subImage2DImplementation)(target, level, offset, Vector2i(image->size(), 1), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } + static void setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector2i& offset, const ImageReference2D& image); + #ifndef MAGNUM_TARGET_GLES2 + static void setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector2i& offset, BufferImage2D& image); + #endif static void invalidateSubImage(AbstractTexture* texture, GLint level, const Vector2i& offset, const Vector2i& size) { (texture->*invalidateSubImageImplementation)(level, {offset, 0}, {size, 1}); @@ -530,17 +509,15 @@ template<> struct MAGNUM_EXPORT AbstractTexture::DataHelper<3> { (texture->*storage3DImplementation)(target, levels, internalFormat, size); } - template static typename std::enable_if::type setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, Image* image) { - (texture->*image3DImplementation)(target, level, internalFormat, image->size(), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } - - template static typename std::enable_if::type setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector3i& offset, Image* image) { - (texture->*subImage3DImplementation)(target, level, offset, image->size(), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } + static void setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, const ImageReference3D& image); + #ifndef MAGNUM_TARGET_GLES2 + static void setImage(AbstractTexture* texture, GLenum target, GLint level, TextureFormat internalFormat, BufferImage3D& image); + #endif - template static typename std::enable_if::type setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector3i& offset, Image* image) { - (texture->*subImage3DImplementation)(target, level, offset, Vector3i(image->size(), 1), image->format(), image->type(), Implementation::ImageHelper::dataOrPixelUnpackBuffer(image)); - } + static void setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector3i& offset, const ImageReference3D& image); + #ifndef MAGNUM_TARGET_GLES2 + static void setSubImage(AbstractTexture* texture, GLenum target, GLint level, const Vector3i& offset, BufferImage3D& image); + #endif static void invalidateSubImage(AbstractTexture* texture, GLint level, const Vector3i& offset, const Vector3i& size) { (texture->*invalidateSubImageImplementation)(level, offset, size); diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index 0bc8a642c..2308568f2 100644 --- a/src/CubeMapTexture.h +++ b/src/CubeMapTexture.h @@ -165,33 +165,47 @@ class CubeMapTexture: public AbstractTexture { * @param coordinate Coordinate * @param level Mip level * @param internalFormat Internal format - * @param image Image, ImageReference, BufferImage or - * Trade::ImageData of the same dimension count + * @param image %Image * @return Pointer to self (for method chaining) * * See Texture::setImage() for more information. */ - template CubeMapTexture* setImage(Coordinate coordinate, Int level, TextureFormat internalFormat, Image* image) { + CubeMapTexture* setImage(Coordinate coordinate, Int level, TextureFormat internalFormat, const ImageReference2D& image) { DataHelper<2>::setImage(this, static_cast(coordinate), level, internalFormat, image); return this; } + #ifndef MAGNUM_TARGET_GLES2 + /** @overload */ + CubeMapTexture* setImage(Coordinate coordinate, Int level, TextureFormat internalFormat, BufferImage2D& image) { + DataHelper<2>::setImage(this, static_cast(coordinate), level, internalFormat, image); + return this; + } + #endif + /** * @brief Set image subdata * @param coordinate Coordinate * @param level Mip level * @param offset Offset where to put data in the texture - * @param image Image, ImageReference, BufferImage or - * Trade::ImageData of the same or one less dimension count + * @param image %Image * @return Pointer to self (for method chaining) * * See Texture::setSubImage() for more information. */ - template CubeMapTexture* setSubImage(Coordinate coordinate, Int level, const Vector2i& offset, const Image* image) { + CubeMapTexture* setSubImage(Coordinate coordinate, Int level, const Vector2i& offset, const ImageReference2D& image) { DataHelper<2>::setSubImage(this, static_cast(coordinate), level, offset, image); return this; } + #ifndef MAGNUM_TARGET_GLES2 + /** @overload */ + CubeMapTexture* setSubImage(Coordinate coordinate, Int level, const Vector2i& offset, BufferImage2D& image) { + DataHelper<2>::setSubImage(this, static_cast(coordinate), level, offset, image); + return this; + } + #endif + /** * @brief Invalidate texture subimage * @param level Mip level diff --git a/src/CubeMapTextureArray.h b/src/CubeMapTextureArray.h index 0f64d3720..7219706c8 100644 --- a/src/CubeMapTextureArray.h +++ b/src/CubeMapTextureArray.h @@ -175,7 +175,13 @@ class CubeMapTextureArray: public AbstractTexture { * * See Texture::setImage() for more information. */ - template CubeMapTextureArray* setImage(Int level, TextureFormat internalFormat, T* image) { + CubeMapTextureArray* setImage(Int level, TextureFormat internalFormat, const ImageReference3D& image) { + DataHelper<3>::setImage(this, GL_TEXTURE_CUBE_MAP_ARRAY, level, internalFormat, image); + return this; + } + + /** @overload */ + CubeMapTextureArray* setImage(Int level, TextureFormat internalFormat, BufferImage3D& image) { DataHelper<3>::setImage(this, GL_TEXTURE_CUBE_MAP_ARRAY, level, internalFormat, image); return this; } @@ -199,27 +205,14 @@ class CubeMapTextureArray: public AbstractTexture { * * @see setSubImage(Int, Coordinate, Int, const Math::Vector<2, Int>&, const Image*) */ - template CubeMapTextureArray* setSubImage(Int level, const Vector3i& offset, const Image* image) { - DataHelper<3>::setSubImage(this, GL_TEXTURE_CUBE_MAP_ARRAY, level, offset, image, Vector3i(Math::Vector())); + CubeMapTextureArray* setSubImage(Int level, const Vector3i& offset, const ImageReference3D& image) { + DataHelper<3>::setSubImage(this, GL_TEXTURE_CUBE_MAP_ARRAY, level, offset, image); return this; } - /** - * @brief Set texture image 2D subdata - * @param layer Array layer - * @param coordinate Coordinate - * @param level Mip level - * @param offset Offset where to put data in the texture - * @param image Image2D, ImageReference2D, BufferImage2D or - * Trade::ImageData2D - * @return Pointer to self (for method chaining) - * - * See Texture::setSubImage() for more information. - * - * @see setSubImage(Int, const Math::Vector<3, Int>&, const Image*) - */ - template CubeMapTextureArray* setSubImage(Int layer, Coordinate coordinate, Int level, const Vector2i& offset, const Image* image) { - DataHelper<3>::setSubImage(this, GL_TEXTURE_CUBE_MAP_ARRAY, level, Vector3i(offset, layer*6+static_cast(coordinate)), image, Vector2i(Math::Vector())); + /** @overload */ + CubeMapTextureArray* setSubImage(Int level, const Vector3i& offset, BufferImage3D& image) { + DataHelper<3>::setSubImage(this, GL_TEXTURE_CUBE_MAP_ARRAY, level, offset, image); return this; } diff --git a/src/Text/DistanceFieldGlyphCache.cpp b/src/Text/DistanceFieldGlyphCache.cpp index 83daad155..78ce1b833 100644 --- a/src/Text/DistanceFieldGlyphCache.cpp +++ b/src/Text/DistanceFieldGlyphCache.cpp @@ -25,10 +25,10 @@ #include "DistanceFieldGlyphCache.h" #include "Extensions.h" -#include "Image.h" #ifndef CORRADE_NO_ASSERT #include "ImageFormat.h" #endif +#include "ImageReference.h" #include "TextureFormat.h" #include "TextureTools/DistanceField.h" @@ -52,25 +52,25 @@ DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& originalSize, c initialize(internalFormat, distanceFieldSize); } -void DistanceFieldGlyphCache::setImage(const Vector2i& offset, Image2D* const image) { +void DistanceFieldGlyphCache::setImage(const Vector2i& offset, const ImageReference2D& image) { #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::texture_rg); #endif #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES3) const TextureFormat internalFormat = TextureFormat::R8; - CORRADE_ASSERT(image->format() == ImageFormat::Red, - "Text::DistanceFieldGlyphCache::setImage(): expected" << ImageFormat::Red << "but got" << image->format(), ); + CORRADE_ASSERT(image.format() == ImageFormat::Red, + "Text::DistanceFieldGlyphCache::setImage(): expected" << ImageFormat::Red << "but got" << image.format(), ); #else TextureFormat internalFormat; if(Context::current()->isExtensionSupported()) { internalFormat = TextureFormat::Red; - CORRADE_ASSERT(image->format() == ImageFormat::Red, - "Text::DistanceFieldGlyphCache::setImage(): expected" << ImageFormat::Red << "but got" << image->format(), ); + CORRADE_ASSERT(image.format() == ImageFormat::Red, + "Text::DistanceFieldGlyphCache::setImage(): expected" << ImageFormat::Red << "but got" << image.format(), ); } else { internalFormat = TextureFormat::Luminance; - CORRADE_ASSERT(image->format() == ImageFormat::Luminance, - "Text::DistanceFieldGlyphCache::setImage(): expected" << ImageFormat::Luminance << "but got" << image->format(), ); + CORRADE_ASSERT(image.format() == ImageFormat::Luminance, + "Text::DistanceFieldGlyphCache::setImage(): expected" << ImageFormat::Luminance << "but got" << image.format(), ); } #endif @@ -81,10 +81,10 @@ void DistanceFieldGlyphCache::setImage(const Vector2i& offset, Image2D* const im ->setImage(0, internalFormat, image); /* Create distance field from input texture */ - TextureTools::distanceField(&input, &_texture, Rectanglei::fromSize(offset*scale, image->size()*scale), radius, image->size()); + TextureTools::distanceField(&input, &_texture, Rectanglei::fromSize(offset*scale, image.size()*scale), radius, image.size()); } -void DistanceFieldGlyphCache::setDistanceFieldImage(const Vector2i& offset, Image2D* const image) { +void DistanceFieldGlyphCache::setDistanceFieldImage(const Vector2i& offset, const ImageReference2D& image) { _texture.setSubImage(0, offset, image); } diff --git a/src/Text/DistanceFieldGlyphCache.h b/src/Text/DistanceFieldGlyphCache.h index bb5d1194c..011d11943 100644 --- a/src/Text/DistanceFieldGlyphCache.h +++ b/src/Text/DistanceFieldGlyphCache.h @@ -78,7 +78,7 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { * Uploads image for one or more glyphs to given offset in original * cache texture. The texture is then converted to distance field. */ - void setImage(const Vector2i& offset, Image2D* image) override; + void setImage(const Vector2i& offset, const ImageReference2D& image) override; /** * @brief Set distance field cache image @@ -86,7 +86,7 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { * Uploads already computed distance field image to given offset in * distance field texture. */ - void setDistanceFieldImage(const Vector2i& offset, Image2D* image); + void setDistanceFieldImage(const Vector2i& offset, const ImageReference2D& image); private: const Vector2 scale; diff --git a/src/Text/GlyphCache.cpp b/src/Text/GlyphCache.cpp index 8d96ef2a3..115e1ece6 100644 --- a/src/Text/GlyphCache.cpp +++ b/src/Text/GlyphCache.cpp @@ -77,7 +77,7 @@ void GlyphCache::insert(const UnsignedInt glyph, Vector2i position, Rectanglei r glyphs.insert({glyph, {position, rectangle}}); } -void GlyphCache::setImage(const Vector2i& offset, Image2D* const image) { +void GlyphCache::setImage(const Vector2i& offset, const ImageReference2D& image) { _texture.setSubImage(0, offset, image); } diff --git a/src/Text/GlyphCache.h b/src/Text/GlyphCache.h index 35d93cf52..39dacc9c0 100644 --- a/src/Text/GlyphCache.h +++ b/src/Text/GlyphCache.h @@ -135,7 +135,7 @@ class MAGNUM_TEXT_EXPORT GlyphCache { * Uploads image for one or more glyphs to given offset in cache * texture. */ - virtual void setImage(const Vector2i& offset, Image2D* image); + virtual void setImage(const Vector2i& offset, const ImageReference2D& image); #ifdef DOXYGEN_GENERATING_OUTPUT private: diff --git a/src/Texture.h b/src/Texture.h index a6c6e0a68..dce833157 100644 --- a/src/Texture.h +++ b/src/Texture.h @@ -346,12 +346,9 @@ template class Texture: public AbstractTexture { * @brief Set image data * @param level Mip level * @param internalFormat Internal format - * @param image Image, ImageReference, BufferImage or - * Trade::ImageData of the same dimension count + * @param image %Image * @return Pointer to self (for method chaining) * - * The image is not deleted afterwards. - * * For better performance when generating mipmaps using * generateMipmap() or calling setImage() more than once use * setStorage() and setSubImage() instead. @@ -364,28 +361,26 @@ template class Texture: public AbstractTexture { * @fn_gl_extension{TextureImage2D,EXT,direct_state_access}/ * @fn_gl_extension{TextureImage3D,EXT,direct_state_access} */ - template Texture* setImage(Int level, TextureFormat internalFormat, Image* image) { + Texture* setImage(Int level, TextureFormat internalFormat, const ImageReference& image) { DataHelper::setImage(this, _target, level, internalFormat, image); return this; } + #ifndef MAGNUM_TARGET_GLES2 + /** @overload */ + Texture* setImage(Int level, TextureFormat internalFormat, BufferImage& image) { + DataHelper::setImage(this, _target, level, internalFormat, image); + return this; + } + #endif + /** * @brief Set image subdata * @param level Mip level * @param offset Offset where to put data in the texture - * @param image Image, ImageReference, BufferImage or - * Trade::ImageData of the same or one less dimension count + * @param image %Image * @return Pointer to self (for method chaining) * - * The image is not deleted afterwards. The image can have either the - * same dimension count or have one dimension less, but at least one - * dimension. - * - * If the image has one dimension less than the texture, the image is - * taken as if it had the last dimension equal to 1. It can be used - * for e.g. updating 3D texture with multiple 2D images or for filling - * 1D texture array (which is two-dimensional) with 1D images. - * * If @extension{EXT,direct_state_access} is not available, the * texture is bound to some layer before the operation. * @see setStorage(), setImage(), @fn_gl{ActiveTexture}, @fn_gl{BindTexture} @@ -394,10 +389,18 @@ template class Texture: public AbstractTexture { * @fn_gl_extension{TextureSubImage2D,EXT,direct_state_access}/ * @fn_gl_extension{TextureSubImage3D,EXT,direct_state_access} */ - template Texture* setSubImage(Int level, const typename DimensionTraits::VectorType& offset, Image* image) { + Texture* setSubImage(Int level, const typename DimensionTraits::VectorType& offset, const ImageReference& image) { + DataHelper::setSubImage(this, _target, level, offset, image); + return this; + } + + #ifndef MAGNUM_TARGET_GLES2 + /** @overload */ + Texture* setSubImage(Int level, const typename DimensionTraits::VectorType& offset, BufferImage& image) { DataHelper::setSubImage(this, _target, level, offset, image); return this; } + #endif /** * @brief Invalidate texture subimage From eb3de49e75982231b0f884687822a8e1377da7d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 01:43:02 +0200 Subject: [PATCH 15/36] Reordered *Image parameters. First format, then type, then size, then data. Seems more intuitive this way, also the data are laid out in the structure this way. --- src/AbstractFramebuffer.cpp | 2 +- src/AbstractTexture.cpp | 2 +- src/Image.cpp | 2 +- src/Image.h | 10 +++++----- src/ImageReference.h | 8 ++++---- src/Test/ImageTest.cpp | 6 +++--- src/Trade/ImageData.h | 6 +++--- src/Trade/Test/AbstractImageConverterTest.cpp | 2 +- src/Trade/Test/ImageDataTest.cpp | 8 ++++---- 9 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/AbstractFramebuffer.cpp b/src/AbstractFramebuffer.cpp index 1097fb10a..3a4ba0bb2 100644 --- a/src/AbstractFramebuffer.cpp +++ b/src/AbstractFramebuffer.cpp @@ -150,7 +150,7 @@ void AbstractFramebuffer::read(const Vector2i& offset, const Vector2i& size, Ima const std::size_t dataSize = image.pixelSize()*size.product(); char* const data = new char[dataSize]; readImplementation(offset, size, image.format(), image.type(), dataSize, data); - image.setData(size, image.format(), image.type(), data); + image.setData(image.format(), image.type(), size, data); } #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/AbstractTexture.cpp b/src/AbstractTexture.cpp index 22c898d27..fc02749d7 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -954,7 +954,7 @@ template void AbstractTexture::image(GLenum target, GLin const std::size_t dataSize = size.product()*image.pixelSize(); char* data = new char[dataSize]; (this->*getImageImplementation)(target, level, image.format(), image.type(), dataSize, data); - image.setData(size, image.format(), image.type(), data); + image.setData(image.format(), image.type(), size, data); } template void AbstractTexture::image<1>(GLenum, GLint, Image<1>&); diff --git a/src/Image.cpp b/src/Image.cpp index 71c5b1a7c..312fbfe4f 100644 --- a/src/Image.cpp +++ b/src/Image.cpp @@ -26,7 +26,7 @@ namespace Magnum { -template void Image::setData(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data) { +template void Image::setData(ImageFormat format, ImageType type, const typename DimensionTraits::VectorType& size, void* data) { delete[] _data; _format = format; _type = type; diff --git a/src/Image.h b/src/Image.h index cc06f228e..b2da03fb2 100644 --- a/src/Image.h +++ b/src/Image.h @@ -45,15 +45,15 @@ template class Image: public AbstractImage { /** * @brief Constructor - * @param size %Image size * @param format Format of pixel data * @param type Data type of pixel data + * @param size %Image size * @param data %Image data * * Note that the image data are not copied on construction, but they * are deleted on class destruction. */ - explicit Image(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} + explicit Image(ImageFormat format, ImageType type, const typename DimensionTraits::VectorType& size, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} /** * @brief Constructor @@ -96,15 +96,15 @@ template class Image: public AbstractImage { /** * @brief Set image data - * @param size %Image size * @param format Format of pixel data * @param type Data type of pixel data + * @param size %Image size * @param data %Image data * * Deletes previous data and replaces them with new. Note that the * data are not copied, but they are deleted on destruction. */ - void setData(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data); + void setData(ImageFormat format, ImageType type, const typename DimensionTraits::VectorType& size, void* data); private: Math::Vector _size; @@ -133,7 +133,7 @@ template inline Image& Image::op } template inline Image::operator ImageReference() const { - return ImageReference(_size, this->format(), this->type(), _data); + return ImageReference(this->format(), this->type(), _size, _data); } } diff --git a/src/ImageReference.h b/src/ImageReference.h index f42498793..8262a80da 100644 --- a/src/ImageReference.h +++ b/src/ImageReference.h @@ -55,26 +55,26 @@ template class ImageReference: public AbstractImage { /** * @brief Constructor - * @param size %Image size * @param format Format of pixel data * @param type Data type of pixel data + * @param size %Image size * @param data %Image data * * Note that the image data are not copied on construction, but they * are deleted on class destruction. */ - constexpr explicit ImageReference(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} + constexpr explicit ImageReference(ImageFormat format, ImageType type, const typename DimensionTraits::VectorType& size, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} /** * @brief Constructor - * @param size %Image size * @param format Format of pixel data * @param type Data type of pixel data + * @param size %Image size * * Data pointer is set to zero, call setData() to fill the image with * data. */ - constexpr explicit ImageReference(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type): AbstractImage(format, type), _size(size), _data(nullptr) {} + constexpr explicit ImageReference(ImageFormat format, ImageType type, const typename DimensionTraits::VectorType& size): AbstractImage(format, type), _size(size), _data(nullptr) {} /** @brief %Image size */ constexpr typename DimensionTraits::VectorType size() const { return _size; } diff --git a/src/Test/ImageTest.cpp b/src/Test/ImageTest.cpp index fbf690f7c..d16f3a578 100644 --- a/src/Test/ImageTest.cpp +++ b/src/Test/ImageTest.cpp @@ -46,7 +46,7 @@ ImageTest::ImageTest() { void ImageTest::moveConstructor() { unsigned char* data = new unsigned char[3]; - Image2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + Image2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, data); Image2D b(std::move(a)); CORRADE_VERIFY(!a.data()); @@ -58,7 +58,7 @@ void ImageTest::moveConstructor() { void ImageTest::moveAssignment() { unsigned char* data = new unsigned char[3]; - Image2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + Image2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, data); Image2D b(ImageFormat::Red, ImageType::UnsignedByte); b = std::move(a); @@ -71,7 +71,7 @@ void ImageTest::moveAssignment() { void ImageTest::toReference() { unsigned char* data = new unsigned char[3]; - Image2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + Image2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, data); ImageReference2D b = a; CORRADE_COMPARE(b.format(), ImageFormat::Red); diff --git a/src/Trade/ImageData.h b/src/Trade/ImageData.h index dd1e2f2a3..91c4e43c6 100644 --- a/src/Trade/ImageData.h +++ b/src/Trade/ImageData.h @@ -45,15 +45,15 @@ template class ImageData: public AbstractImage { /** * @brief Constructor - * @param size %Image size * @param format Format of pixel data * @param type Data type of pixel data + * @param size %Image size * @param data %Image data * * Note that the image data are not copied on construction, but they * are deleted on class destruction. */ - explicit ImageData(const typename DimensionTraits::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} + explicit ImageData(ImageFormat format, ImageType type, const typename DimensionTraits::VectorType& size, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast(data)) {} /** @brief Copying is not allowed */ ImageData(const ImageData&& other) = delete; @@ -111,7 +111,7 @@ template inline ImageData& ImageData inline ImageData::operator ImageReference() const { - return ImageReference(_size, this->format(), this->type(), _data); + return ImageReference(this->format(), this->type(), _size, _data); } }} diff --git a/src/Trade/Test/AbstractImageConverterTest.cpp b/src/Trade/Test/AbstractImageConverterTest.cpp index 3d209ea79..7fbfae7c3 100644 --- a/src/Trade/Test/AbstractImageConverterTest.cpp +++ b/src/Trade/Test/AbstractImageConverterTest.cpp @@ -64,7 +64,7 @@ void AbstractImageConverterTest::exportToFile() { /* doExportToFile() should call doExportToData() */ DataExporter exporter; - Image2D image({0xfe, 0xed}, ImageFormat::RGBA, ImageType::UnsignedByte, nullptr); + Image2D image(ImageFormat::RGBA, ImageType::UnsignedByte, {0xfe, 0xed}, 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); diff --git a/src/Trade/Test/ImageDataTest.cpp b/src/Trade/Test/ImageDataTest.cpp index b2d428a9e..76c836b1e 100644 --- a/src/Trade/Test/ImageDataTest.cpp +++ b/src/Trade/Test/ImageDataTest.cpp @@ -46,7 +46,7 @@ ImageDataTest::ImageDataTest() { void ImageDataTest::moveConstructor() { unsigned char* data = new unsigned char[3]; - Trade::ImageData2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + Trade::ImageData2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, data); Trade::ImageData2D b(std::move(a)); CORRADE_VERIFY(!a.data()); @@ -58,9 +58,9 @@ void ImageDataTest::moveConstructor() { void ImageDataTest::moveAssignment() { unsigned char* data = new unsigned char[3]; - Trade::ImageData2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + Trade::ImageData2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, data); - Trade::ImageData2D b({}, ImageFormat::Red, ImageType::UnsignedByte, nullptr); + Trade::ImageData2D b(ImageFormat::Red, ImageType::UnsignedByte, {}, nullptr); b = std::move(a); CORRADE_VERIFY(!a.data()); CORRADE_COMPARE(b.format(), ImageFormat::Red); @@ -71,7 +71,7 @@ void ImageDataTest::moveAssignment() { void ImageDataTest::toReference() { unsigned char* data = new unsigned char[3]; - Trade::ImageData2D a({1, 3}, ImageFormat::Red, ImageType::UnsignedByte, data); + Trade::ImageData2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, data); ImageReference2D b = a; CORRADE_COMPARE(b.format(), ImageFormat::Red); From 9ccc63b4aa2df14c9ea0e06892b2cf0158133119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 02:04:52 +0200 Subject: [PATCH 16/36] Math: fixed strict aliasing warning. Is there _any_ use case of reinterpret_cast where GCC won't loudly complain? --- src/Math/Geometry/Rectangle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Math/Geometry/Rectangle.h b/src/Math/Geometry/Rectangle.h index 41c3a4efc..21f3bb331 100644 --- a/src/Math/Geometry/Rectangle.h +++ b/src/Math/Geometry/Rectangle.h @@ -163,7 +163,7 @@ template struct ConfigurationValue /** @brief Reads elements separated with whitespace */ static Magnum::Math::Geometry::Rectangle fromString(const std::string& stringValue, const ConfigurationValueFlags flags) { const auto vec = ConfigurationValue>::fromString(stringValue, flags); - return reinterpret_cast&>(vec); + return {{vec[0], vec[1]}, {vec[2], vec[3]}}; } }; From 8ab5711d16e8a0e8cc9f710ab63debd4b98f1d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 25 May 2013 15:23:44 +0200 Subject: [PATCH 17/36] Initial support for GL tests, initial Buffer test. --- CMakeLists.txt | 11 ++ src/Test/AbstractOpenGLTester.h | 56 +++++++ src/Test/BufferGLTest.cpp | 265 ++++++++++++++++++++++++++++++++ src/Test/CMakeLists.txt | 4 + 4 files changed, 336 insertions(+) create mode 100644 src/Test/AbstractOpenGLTester.h create mode 100644 src/Test/BufferGLTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 36cf5ac91..3c5ac343e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ endif() option(BUILD_STATIC "Build static libraries (default are shared)" OFF) cmake_dependent_option(BUILD_STATIC_PIC "Build static libraries with position-independent code" OFF "BUILD_STATIC" OFF) option(BUILD_TESTS "Build unit tests." OFF) +cmake_dependent_option(BUILD_GL_TESTS "Build unit tests for OpenGL code." OFF "BUILD_TESTS" OFF) if(BUILD_TESTS) enable_testing() endif() @@ -107,6 +108,16 @@ if(TARGET_DESKTOP_GLES) set(MAGNUM_TARGET_DESKTOP_GLES 1) endif() +if(BUILD_GL_TESTS) + if(UNIX AND (NOT MAGNUM_TARGET_GLES OR MAGNUM_TARGET_DESKTOP_GLES)) + set(WITH_WINDOWLESSGLXAPPLICATION ON) + find_package(X11 REQUIRED) + set(GL_TEST_LIBRARIES Magnum MagnumWindowlessGlxApplication ${X11_LIBRARIES}) + else() + message(FATAL_ERROR "Cannot run tests for OpenGL code on this platform. Set BUILD_GL_TESTS to OFF to skip building them.") + endif() +endif() + if(NOT BUILD_STATIC) set(SHARED_OR_STATIC SHARED) else() diff --git a/src/Test/AbstractOpenGLTester.h b/src/Test/AbstractOpenGLTester.h new file mode 100644 index 000000000..fe23735a2 --- /dev/null +++ b/src/Test/AbstractOpenGLTester.h @@ -0,0 +1,56 @@ +#ifndef Magnum_Test_AbstractOpenGLTester_h +#define Magnum_Test_AbstractOpenGLTester_h +/* + 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 "Renderer.h" + +#if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_DESKTOP_GLES) +#include "Platform/WindowlessGlxApplication.h" +#else +#error Cannot run OpenGL tests on this platform +#endif + +namespace Magnum { namespace Test { + +class AbstractOpenGLTester: public TestSuite::Tester, public Platform::WindowlessApplication { + public: + explicit AbstractOpenGLTester(): Platform::WindowlessApplication({zero, nullptr}) {} + + using TestSuite::Tester::exec; + int exec() override { return TestSuite::Tester::exec(); } + + private: + static int zero; +}; + +int AbstractOpenGLTester::zero = 0; + +#define MAGNUM_VERIFY_NO_ERROR() CORRADE_COMPARE(Renderer::error(), Renderer::Error::NoError) + +}} + +#endif diff --git a/src/Test/BufferGLTest.cpp b/src/Test/BufferGLTest.cpp new file mode 100644 index 000000000..8e40094d1 --- /dev/null +++ b/src/Test/BufferGLTest.cpp @@ -0,0 +1,265 @@ +/* + 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 "Buffer.h" +#include "Context.h" +#include "Extensions.h" +#include "Test/AbstractOpenGLTester.h" + +namespace Magnum { namespace Test { + +class BufferGLTest: public AbstractOpenGLTester { + public: + explicit BufferGLTest(); + + void construct(); + void data(); + void map(); + void mapRange(); + void mapRangeExplicitFlush(); + #ifndef MAGNUM_TARGET_GLES2 + void copy(); + #endif + #ifndef MAGNUM_TARGET_GLES2 + void invalidate(); + #endif +}; + +BufferGLTest::BufferGLTest() { + addTests({&BufferGLTest::construct, + &BufferGLTest::data, + &BufferGLTest::map, + &BufferGLTest::mapRange, + &BufferGLTest::mapRangeExplicitFlush, + #ifndef MAGNUM_TARGET_GLES2 + &BufferGLTest::copy, + #endif + #ifndef MAGNUM_TARGET_GLES + &BufferGLTest::invalidate + #endif + }); +} + +void BufferGLTest::construct() { + Buffer buffer; + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(buffer.targetHint(), Buffer::Target::Array); + + CORRADE_COMPARE(buffer.size(), 0); + MAGNUM_VERIFY_NO_ERROR(); +} + +void BufferGLTest::data() { + Buffer buffer; + + constexpr char data[] = {2, 7, 5, 13, 25}; + buffer.setData(5, data, Buffer::Usage::StaticDraw); + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(buffer.size(), 5); + + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + const Containers::Array contents = buffer.data(); + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(contents.size(), 5); + CORRADE_COMPARE(contents[0], 2); + CORRADE_COMPARE(contents[1], 7); + CORRADE_COMPARE(contents[2], 5); + CORRADE_COMPARE(contents[3], 13); + CORRADE_COMPARE(contents[4], 25); + #endif + + constexpr char subData[] = {125, 3, 15}; + buffer.setSubData(1, 3, subData); + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(buffer.size(), 5); + + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + const Containers::Array subContents = buffer.subData(1, 3); + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(subContents.size(), 3); + CORRADE_COMPARE(subContents[0], 125); + CORRADE_COMPARE(subContents[1], 3); + CORRADE_COMPARE(subContents[2], 15); + #endif +} + +#ifndef MAGNUM_TARGET_GLES3 +void BufferGLTest::map() { + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::OES::mapbuffer::string() + std::string(" is not supported")); + #endif + Buffer buffer; + + constexpr char data[] = {2, 7, 5, 13, 25}; + buffer.setData(data, Buffer::Usage::StaticDraw); + + #ifndef MAGNUM_TARGET_GLES2 + char* contents = reinterpret_cast(buffer.map(Buffer::MapAccess::ReadWrite)); + #else + char* contents = reinterpret_cast(buffer.map(Buffer::MapAccess::WriteOnly)); + #endif + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_VERIFY(contents); + #ifndef MAGNUM_TARGET_GLES2 + CORRADE_COMPARE(contents[2], 5); + #endif + contents[3] = 107; + + CORRADE_VERIFY(buffer.unmap()); + MAGNUM_VERIFY_NO_ERROR(); + + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + Containers::Array changedContents = buffer.data(); + CORRADE_COMPARE(changedContents.size(), 5); + CORRADE_COMPARE(changedContents[3], 107); + #endif +} +#endif + +void BufferGLTest::mapRange() { + #ifndef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported")); + #else + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported")); + #endif + + constexpr char data[] = {2, 7, 5, 13, 25}; + Buffer buffer; + buffer.setData(data, Buffer::Usage::StaticDraw); + + char* contents = reinterpret_cast(buffer.map(1, 4, Buffer::MapFlag::Read|Buffer::MapFlag::Write)); + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_VERIFY(contents); + CORRADE_COMPARE(contents[2], 13); + contents[3] = 107; + + CORRADE_VERIFY(buffer.unmap()); + MAGNUM_VERIFY_NO_ERROR(); + + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + Containers::Array changedContents = buffer.data(); + CORRADE_COMPARE(changedContents.size(), 5); + CORRADE_COMPARE(changedContents[4], 107); + #endif +} + +void BufferGLTest::mapRangeExplicitFlush() { + #ifndef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported")); + #else + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::EXT::map_buffer_range::string() + std::string(" is not supported")); + #endif + + constexpr char data[] = {2, 7, 5, 13, 25}; + Buffer buffer; + buffer.setData(data, Buffer::Usage::StaticDraw); + + /* Map, set byte, don't flush and unmap */ + char* contents = reinterpret_cast(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit)); + CORRADE_VERIFY(contents); + contents[2] = 99; + CORRADE_VERIFY(buffer.unmap()); + MAGNUM_VERIFY_NO_ERROR(); + + /* Unflushed range _might_ not be changed, thus nothing to test */ + + /* Map, set byte, flush and unmap */ + contents = reinterpret_cast(buffer.map(1, 4, Buffer::MapFlag::Write|Buffer::MapFlag::FlushExplicit)); + CORRADE_VERIFY(contents); + contents[3] = 107; + buffer.flushMappedRange(3, 1); + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_VERIFY(buffer.unmap()); + MAGNUM_VERIFY_NO_ERROR(); + + /* Flushed range should be changed */ + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + Containers::Array changedContents = buffer.data(); + CORRADE_COMPARE(changedContents.size(), 5); + CORRADE_COMPARE(changedContents[4], 107); + #endif +} + +#ifndef MAGNUM_TARGET_GLES2 +void BufferGLTest::copy() { + Buffer buffer1; + constexpr char data[] = {2, 7, 5, 13, 25}; + buffer1.setData(data, Buffer::Usage::StaticDraw); + + Buffer buffer2; + buffer2.setData(5, nullptr, Buffer::Usage::StaticDraw); + + Buffer::copy(&buffer1, &buffer2, 1, 2, 3); + MAGNUM_VERIFY_NO_ERROR(); + + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + const Containers::Array subContents = buffer2.subData(2, 3); + CORRADE_COMPARE(subContents.size(), 3); + CORRADE_COMPARE(subContents[0], 7); + CORRADE_COMPARE(subContents[1], 5); + CORRADE_COMPARE(subContents[2], 13); + #endif +} +#endif + +#ifndef MAGNUM_TARGET_GLES +void BufferGLTest::invalidate() { + if(!Context::current()->isExtensionSupported()) + CORRADE_SKIP(Extensions::GL::ARB::invalidate_subdata::string() + std::string(" is not supported")); + + Buffer buffer; + constexpr char data[] = {2, 7, 5, 13, 25}; + buffer.setData(data, Buffer::Usage::StaticDraw); + + /* Just test that no errors are emitted */ + + buffer.invalidateSubData(3, 2); + MAGNUM_VERIFY_NO_ERROR(); + + buffer.invalidateData(); + MAGNUM_VERIFY_NO_ERROR(); +} +#endif + +}} + +CORRADE_TEST_MAIN(Magnum::Test::BufferGLTest) diff --git a/src/Test/CMakeLists.txt b/src/Test/CMakeLists.txt index 3998a58ee..d4847a633 100644 --- a/src/Test/CMakeLists.txt +++ b/src/Test/CMakeLists.txt @@ -34,4 +34,8 @@ corrade_add_test(RendererTest RendererTest.cpp LIBRARIES Magnum) corrade_add_test(ResourceManagerTest ResourceManagerTest.cpp LIBRARIES MagnumTestLib) corrade_add_test(SwizzleTest SwizzleTest.cpp LIBRARIES MagnumMathTestLib) +if(BUILD_GL_TESTS) + corrade_add_test(BufferGLTest BufferGLTest.cpp LIBRARIES ${GL_TEST_LIBRARIES}) +endif() + set_target_properties(ResourceManagerTest PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) From a17d7f27058b263a5a304c65fef4bf95e8b688fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 02:30:15 +0200 Subject: [PATCH 18/36] Install also bootstrap header for GL tests if BUILD_GL_TESTS is enabled. --- src/Test/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Test/CMakeLists.txt b/src/Test/CMakeLists.txt index d4847a633..ca308059b 100644 --- a/src/Test/CMakeLists.txt +++ b/src/Test/CMakeLists.txt @@ -39,3 +39,8 @@ if(BUILD_GL_TESTS) endif() set_target_properties(ResourceManagerTest PROPERTIES COMPILE_FLAGS -DCORRADE_GRACEFUL_ASSERT) + +# Install bootstrap header for GL tests to be used in dependent projects +if(BUILD_GL_TESTS) + install(FILES AbstractOpenGLTester.h DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/Test) +endif() From 5f0d9297e2616347a1f0c5211d45c6e694bb6bc0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 17:29:18 +0200 Subject: [PATCH 19/36] Templated version of Buffer::{data,subData}(). More convenient for end user, assert that buffer size is divisible by T size to avoid some issues. --- src/Buffer.cpp | 13 ------------- src/Buffer.h | 27 +++++++++++++++++++++------ src/Test/BufferGLTest.cpp | 24 ++++++++++++------------ 3 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/Buffer.cpp b/src/Buffer.cpp index 1bdbaa335..69bddf75c 100644 --- a/src/Buffer.cpp +++ b/src/Buffer.cpp @@ -24,7 +24,6 @@ #include "Buffer.h" -#include #include #include "Context.h" @@ -128,18 +127,6 @@ Int Buffer::size() { return size; } -#ifndef MAGNUM_TARGET_GLES -Containers::Array Buffer::data() { - return subData(0, size()); -} - -Containers::Array Buffer::subData(const GLintptr offset, const GLsizeiptr size) { - Containers::Array data(size); - if(size) (this->*getSubDataImplementation)(offset, size, data); - return std::move(data); -} -#endif - #ifndef MAGNUM_TARGET_GLES2 void Buffer::copyImplementationDefault(Buffer* read, Buffer* write, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size) { glCopyBufferSubData(static_cast(read->bindInternal(Target::CopyRead)), static_cast(write->bindInternal(Target::CopyWrite)), readOffset, writeOffset, size); diff --git a/src/Buffer.h b/src/Buffer.h index 00001239f..5f16b13a1 100644 --- a/src/Buffer.h +++ b/src/Buffer.h @@ -31,8 +31,9 @@ #include #include #include -#include +#include #include +#include #include "Magnum.h" #include "OpenGL.h" @@ -546,12 +547,12 @@ class MAGNUM_EXPORT Buffer { * @requires_gl %Buffer data queries are not available in OpenGL ES. * Use @ref Magnum::Buffer::map() "map()" instead. */ - Containers::Array data(); + template Containers::Array data(); /** * @brief %Buffer subdata - * @param offset Offset in the buffer - * @param size Data size + * @param offset Byte offset in the buffer + * @param size Data size (count of @p T values) * * Returns data of given buffer portion. If @extension{EXT,direct_state_access} * is not available and the buffer is not already bound somewhere, it @@ -561,7 +562,7 @@ class MAGNUM_EXPORT Buffer { * @requires_gl %Buffer data queries are not available in OpenGL ES. * Use @ref Magnum::Buffer::map() "map()" instead. */ - Containers::Array subData(GLintptr offset, GLsizeiptr size); + template Containers::Array subData(GLintptr offset, GLsizeiptr size); #endif /** @@ -814,7 +815,7 @@ class MAGNUM_EXPORT Buffer { typedef void(Buffer::*GetSubDataImplementation)(GLintptr, GLsizeiptr, GLvoid*); void MAGNUM_LOCAL getSubDataImplementationDefault(GLintptr offset, GLsizeiptr size, GLvoid* data); void MAGNUM_LOCAL getSubDataImplementationDSA(GLintptr offset, GLsizeiptr size, GLvoid* data); - static MAGNUM_LOCAL GetSubDataImplementation getSubDataImplementation; + static GetSubDataImplementation getSubDataImplementation; #endif typedef void(Buffer::*DataImplementation)(GLsizeiptr, const GLvoid*, Usage); @@ -884,6 +885,20 @@ CORRADE_ENUMSET_OPERATORS(Buffer::MapFlags) /** @debugoperator{Magnum::Buffer} */ Debug MAGNUM_EXPORT operator<<(Debug debug, Buffer::Target value); +#ifndef MAGNUM_TARGET_GLES +template Containers::Array inline Buffer::data() { + const Int bufferSize = size(); + CORRADE_ASSERT(bufferSize%sizeof(T) == 0, "Buffer::data(): the buffer size is" << bufferSize << "bytes, which can't be expressed as array of types with size" << sizeof(T), {}); + return subData(0, bufferSize/sizeof(T)); +} + +template Containers::Array inline Buffer::subData(const GLintptr offset, const GLsizeiptr size) { + Containers::Array data(size); + if(size) (this->*getSubDataImplementation)(offset, size*sizeof(T), data); + return std::move(data); +} +#endif + } #endif diff --git a/src/Test/BufferGLTest.cpp b/src/Test/BufferGLTest.cpp index 8e40094d1..a87a06da7 100644 --- a/src/Test/BufferGLTest.cpp +++ b/src/Test/BufferGLTest.cpp @@ -76,15 +76,15 @@ void BufferGLTest::construct() { void BufferGLTest::data() { Buffer buffer; - constexpr char data[] = {2, 7, 5, 13, 25}; - buffer.setData(5, data, Buffer::Usage::StaticDraw); + constexpr Int data[] = {2, 7, 5, 13, 25}; + buffer.setData(5*4, data, Buffer::Usage::StaticDraw); MAGNUM_VERIFY_NO_ERROR(); - CORRADE_COMPARE(buffer.size(), 5); + CORRADE_COMPARE(buffer.size(), 5*4); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES - const Containers::Array contents = buffer.data(); + const Containers::Array contents = buffer.data(); MAGNUM_VERIFY_NO_ERROR(); CORRADE_COMPARE(contents.size(), 5); CORRADE_COMPARE(contents[0], 2); @@ -94,15 +94,15 @@ void BufferGLTest::data() { CORRADE_COMPARE(contents[4], 25); #endif - constexpr char subData[] = {125, 3, 15}; - buffer.setSubData(1, 3, subData); + constexpr Int subData[] = {125, 3, 15}; + buffer.setSubData(4, 3*4, subData); MAGNUM_VERIFY_NO_ERROR(); - CORRADE_COMPARE(buffer.size(), 5); + CORRADE_COMPARE(buffer.size(), 5*4); /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES - const Containers::Array subContents = buffer.subData(1, 3); + const Containers::Array subContents = buffer.subData(4, 3); MAGNUM_VERIFY_NO_ERROR(); CORRADE_COMPARE(subContents.size(), 3); CORRADE_COMPARE(subContents[0], 125); @@ -140,7 +140,7 @@ void BufferGLTest::map() { /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES - Containers::Array changedContents = buffer.data(); + Containers::Array changedContents = buffer.data(); CORRADE_COMPARE(changedContents.size(), 5); CORRADE_COMPARE(changedContents[3], 107); #endif @@ -172,7 +172,7 @@ void BufferGLTest::mapRange() { /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES - Containers::Array changedContents = buffer.data(); + Containers::Array changedContents = buffer.data(); CORRADE_COMPARE(changedContents.size(), 5); CORRADE_COMPARE(changedContents[4], 107); #endif @@ -212,7 +212,7 @@ void BufferGLTest::mapRangeExplicitFlush() { /* Flushed range should be changed */ /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES - Containers::Array changedContents = buffer.data(); + Containers::Array changedContents = buffer.data(); CORRADE_COMPARE(changedContents.size(), 5); CORRADE_COMPARE(changedContents[4], 107); #endif @@ -232,7 +232,7 @@ void BufferGLTest::copy() { /** @todo How to verify the contents in ES? */ #ifndef MAGNUM_TARGET_GLES - const Containers::Array subContents = buffer2.subData(2, 3); + const Containers::Array subContents = buffer2.subData(2, 3); CORRADE_COMPARE(subContents.size(), 3); CORRADE_COMPARE(subContents[0], 7); CORRADE_COMPARE(subContents[1], 5); From d3d172daec4a5ab096aa1b2e32fda220bc4307f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 01:55:14 +0200 Subject: [PATCH 20/36] Trade: Doxygen fixes. --- src/Trade/AbstractImporter.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Trade/AbstractImporter.h b/src/Trade/AbstractImporter.h index a82d57a3c..606869f04 100644 --- a/src/Trade/AbstractImporter.h +++ b/src/Trade/AbstractImporter.h @@ -98,8 +98,6 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { /** * @brief Open raw data - * @param data Data - * @param size Data size * * Closes previous file, if it was opened, and tries to open given * file. Available only if @ref Feature "Feature::OpenData" is @@ -110,7 +108,6 @@ class MAGNUM_EXPORT AbstractImporter: public PluginManager::AbstractPlugin { /** * @brief Open file - * @param filename Filename * * Closes previous file, if it was opened, and tries to open given * file. Returns `true` on success, `false` otherwise. From b9b690b611e2ad5ca654e78916d6ba7f676cb27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 01:58:35 +0200 Subject: [PATCH 21/36] Use emplace() instead of insert() in some places. Leads to (possibly faster) and shorter code. --- src/Context.cpp | 2 +- src/MeshTools/RemoveDuplicates.h | 2 +- src/ResourceManager.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Context.cpp b/src/Context.cpp index 775ddb65c..7c7a76225 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -337,7 +337,7 @@ Context::Context() { std::unordered_map futureExtensions; for(std::size_t i = future; i != versions.size(); ++i) for(const Extension& extension: Extension::extensions(versions[i])) - futureExtensions.insert(std::make_pair(extension._string, extension)); + futureExtensions.emplace(extension._string, extension); /* Check for presence of extensions in future versions */ #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/MeshTools/RemoveDuplicates.h b/src/MeshTools/RemoveDuplicates.h index c18fdcc88..d51c7e61c 100644 --- a/src/MeshTools/RemoveDuplicates.h +++ b/src/MeshTools/RemoveDuplicates.h @@ -127,7 +127,7 @@ template void RemoveDuplicates, HashedVertex>(Math::Vector::from(index), v)); + auto result = table.emplace(Math::Vector::from(index), v); *it = result.first->second.newIndex; } diff --git a/src/ResourceManager.h b/src/ResourceManager.h index 8038ff458..261566ff5 100644 --- a/src/ResourceManager.h +++ b/src/ResourceManager.h @@ -469,7 +469,7 @@ template void ResourceManagerData::set(const ResourceKey key, T* con /* Insert it, if not already here */ } else if(it == _data.end()) - it = _data.insert(std::make_pair(key, Data())).first; + it = _data.emplace(key, Data()).first; /* Replace previous data */ delete it->second.data; From d6a64cca2a4de3f5212ffe1e8d51633194c14db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Fri, 21 Jun 2013 18:05:46 +0200 Subject: [PATCH 22/36] Text: interface for font converters. Provides a way to convert font into different format (either with or without contents of associated glyph cache) or import/export glyph cache (i.e. to avoid recreating it from scratch every time). --- modules/FindMagnum.cmake | 7 + src/Text/AbstractFontConverter.cpp | 216 ++++++++++++++ src/Text/AbstractFontConverter.h | 313 ++++++++++++++++++++ src/Text/CMakeLists.txt | 2 + src/Text/Test/AbstractFontConverterTest.cpp | 202 +++++++++++++ src/Text/Test/CMakeLists.txt | 1 + src/Text/Test/testConfigure.h.cmake | 1 + src/Text/Text.h | 1 + 8 files changed, 743 insertions(+) create mode 100644 src/Text/AbstractFontConverter.cpp create mode 100644 src/Text/AbstractFontConverter.h create mode 100644 src/Text/Test/AbstractFontConverterTest.cpp diff --git a/modules/FindMagnum.cmake b/modules/FindMagnum.cmake index 9b72c8fec..a3b33eb85 100644 --- a/modules/FindMagnum.cmake +++ b/modules/FindMagnum.cmake @@ -8,6 +8,7 @@ # MAGNUM_INCLUDE_DIRS - Root include dir and include dirs of # dependencies # MAGNUM_PLUGINS_FONT_DIR - Directory with font plugins +# MAGNUM_PLUGINS_FONTCONVERTER_DIR - Directory with font converter plugins # MAGNUM_PLUGINS_IMAGECONVERTER_DIR - Directory with image converter plugins # MAGNUM_PLUGINS_IMPORTER_DIR - Directory with importer plugins # This command will try to find only the base library, not the optional @@ -63,6 +64,8 @@ # MAGNUM_PLUGINS_INSTALL_DIR - Plugin installation directory # MAGNUM_PLUGINS_FONT_INSTALL_DIR - Font plugin installation # directory +# MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR - Font converter plugin +# installation directory # MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR - Image converter plugin # installation directory # MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR - Importer plugin installation @@ -318,6 +321,7 @@ endif() set(MAGNUM_LIBRARY_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) set(MAGNUM_PLUGINS_INSTALL_DIR ${MAGNUM_LIBRARY_INSTALL_DIR}/magnum) set(MAGNUM_PLUGINS_FONT_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fonts) +set(MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers) set(MAGNUM_CMAKE_MODULE_INSTALL_DIR ${CMAKE_ROOT}/Modules) @@ -329,6 +333,7 @@ mark_as_advanced(FORCE MAGNUM_LIBRARY_INSTALL_DIR MAGNUM_PLUGINS_INSTALL_DIR MAGNUM_PLUGINS_FONT_INSTALL_DIR + MAGNUM_PLUGINS_FONTCONVERTER_INSTALL_DIR MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR MAGNUM_CMAKE_MODULE_INSTALL_DIR @@ -338,10 +343,12 @@ mark_as_advanced(FORCE # Plugin directories if(NOT WIN32) set(MAGNUM_PLUGINS_FONT_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fonts) + set(MAGNUM_PLUGINS_FONTCONVERTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/imageconverters) set(MAGNUM_PLUGINS_IMPORTER_DIR ${MAGNUM_PLUGINS_INSTALL_DIR}/importers) else() set(MAGNUM_PLUGINS_FONT_DIR fonts) + set(MAGNUM_PLUGINS_FONTCONVERTER_DIR fontconverters) set(MAGNUM_PLUGINS_IMAGECONVERTER_DIR imageconverters) set(MAGNUM_PLUGINS_IMPORTER_DIR importers) endif() diff --git a/src/Text/AbstractFontConverter.cpp b/src/Text/AbstractFontConverter.cpp new file mode 100644 index 000000000..ebd8cca31 --- /dev/null +++ b/src/Text/AbstractFontConverter.cpp @@ -0,0 +1,216 @@ +/* + 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 "AbstractFontConverter.h" + +namespace Magnum { namespace Text { + +AbstractFontConverter::AbstractFontConverter() = default; + +AbstractFontConverter::AbstractFontConverter(PluginManager::AbstractManager* manager, std::string plugin): PluginManager::AbstractPlugin(manager, std::move(plugin)) {} + +std::vector>> AbstractFontConverter::exportFontToData(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::string& characters) const { + CORRADE_ASSERT(features() >= (Feature::ExportFont|Feature::ConvertData), + "Text::AbstractFontConverter::exportFontToData(): feature not supported", {}); + + return doExportFontToData(font, cache, filename, Utility::Unicode::utf32(characters)); +} + +std::vector>> AbstractFontConverter::doExportFontToData(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::u32string& characters) const { + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportFontToData(): feature advertised but not implemented", {}); + + std::vector>> out; + out.emplace_back(filename, std::move(doExportFontToSingleData(font, cache, characters))); + return std::move(out); +} + +Containers::Array AbstractFontConverter::exportFontToSingleData(AbstractFont* const font, GlyphCache* const cache, const std::string& characters) const { + CORRADE_ASSERT(features() >= (Feature::ExportFont|Feature::ConvertData), + "Text::AbstractFontConverter::exportFontToSingleData(): feature not supported", nullptr); + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportFontToSingleData(): the format is not single-file", nullptr); + + return doExportFontToSingleData(font, cache, Utility::Unicode::utf32(characters)); +} + +Containers::Array AbstractFontConverter::doExportFontToSingleData(AbstractFont*, GlyphCache*, const std::u32string&) const { + CORRADE_ASSERT(false, + "Text::AbstractFontConverter::exportFontToSingleData(): feature advertised but not implemented", nullptr); +} + +bool AbstractFontConverter::exportFontToFile(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::string& characters) const { + CORRADE_ASSERT(features() & Feature::ExportFont, + "Text::AbstractFontConverter::exportFontToFile(): feature not supported", false); + + return doExportFontToFile(font, cache, filename, Utility::Unicode::utf32(characters)); +} + +bool AbstractFontConverter::doExportFontToFile(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::u32string& characters) const { + CORRADE_ASSERT(features() & Feature::ConvertData, + "Text::AbstractFontConverter::exportFontToFile(): not implemented", false); + + /* Export all data */ + const auto data = doExportFontToData(font, cache, filename, characters); + for(const auto& d: data) { + /* Open file */ + std::ofstream out(d.first.data(), std::ios::binary); + if(!out.good()) { + Error() << "Text::AbstractFontConverter::exportFontToFile(): cannot write to file" << d.first; + return false; + } + + /* Write data, close */ + out.write(reinterpret_cast(d.second.begin()), d.second.size()); + } + + return true; +} + +std::vector>> AbstractFontConverter::exportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(features() >= (Feature::ExportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::exportGlyphCacheToData(): feature not supported", {}); + + return doExportGlyphCacheToData(cache, filename); +} + +std::vector>> AbstractFontConverter::doExportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportGlyphCacheToData(): feature advertised but not implemented", {}); + + std::vector>> out; + out.emplace_back(filename, std::move(doExportGlyphCacheToSingleData(cache))); + return std::move(out); +} + +Containers::Array AbstractFontConverter::exportGlyphCacheToSingleData(GlyphCache* cache) const { + CORRADE_ASSERT(features() >= (Feature::ExportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): feature not supported", nullptr); + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): the format is not single-file", nullptr); + + return doExportGlyphCacheToSingleData(cache); +} + +Containers::Array AbstractFontConverter::doExportGlyphCacheToSingleData(GlyphCache*) const { + CORRADE_ASSERT(false, + "Text::AbstractFontConverter::exportGlyphCacheToSingleData(): feature advertised but not implemented", nullptr); +} + +bool AbstractFontConverter::exportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ExportGlyphCache, + "Text::AbstractFontConverter::exportGlyphCacheToFile(): feature not supported", false); + + return doExportGlyphCacheToFile(cache, filename); +} + +bool AbstractFontConverter::doExportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ConvertData, + "Text::AbstractFontConverter::exportGlyphCacheToFile(): not implemented", false); + + /* Export all data */ + const auto data = doExportGlyphCacheToData(cache, filename); + for(const auto& d: data) { + /* Open file */ + std::ofstream out(d.first.data(), std::ios::binary); + if(!out.good()) { + Error() << "Text::AbstractFontConverter::exportGlyphCacheToFile(): cannot write to file" << d.first; + return false; + } + + /* Write data, close */ + out.write(reinterpret_cast(d.second.begin()), d.second.size()); + } + + return true; +} + +GlyphCache* AbstractFontConverter::importGlyphCacheFromData(const std::vector>>& data) const { + CORRADE_ASSERT(features() >= (Feature::ImportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::importGlyphCacheFromData(): feature not supported", nullptr); + CORRADE_ASSERT(!data.empty(), + "Text::AbstractFontConverter::importGlyphCacheFromData(): no data passed", nullptr); + + return doImportGlyphCacheFromData(data); +} + +GlyphCache* AbstractFontConverter::doImportGlyphCacheFromData(const std::vector>>& data) const { + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::importGlyphCacheFromData(): feature advertised but not implemented", nullptr); + CORRADE_ASSERT(data.size() == 1, + "Text::AbstractFontConverter::importGlyphCacheFromData(): expected just one file for single-file format", nullptr); + + return doImportGlyphCacheFromSingleData(data[0].second); +} + +GlyphCache* AbstractFontConverter::importGlyphCacheFromSingleData(Containers::ArrayReference data) const { + CORRADE_ASSERT(features() >= (Feature::ImportGlyphCache|Feature::ConvertData), + "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): feature not supported", nullptr); + CORRADE_ASSERT(!(features() & Feature::MultiFile), + "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): the format is not single-file", nullptr); + + return doImportGlyphCacheFromSingleData(data); +} + +GlyphCache* AbstractFontConverter::doImportGlyphCacheFromSingleData(Containers::ArrayReference) const { + CORRADE_ASSERT(false, + "Text::AbstractFontConverter::importGlyphCacheFromSingleData(): feature advertised but not implemented", nullptr); +} + +GlyphCache* AbstractFontConverter::importGlyphCacheFromFile(const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ImportGlyphCache, + "Text::AbstractFontConverter::importGlyphCacheFromFile(): feature not supported", nullptr); + + return doImportGlyphCacheFromFile(filename); +} + +GlyphCache* AbstractFontConverter::doImportGlyphCacheFromFile(const std::string& filename) const { + CORRADE_ASSERT(features() & Feature::ConvertData && !(features() & Feature::MultiFile), + "Text::AbstractFontConverter::importGlyphCacheFromFile(): not implemented", nullptr); + + /* Open file */ + std::ifstream in(filename.data(), std::ios::binary); + if(!in.good()) { + Error() << "Trade::AbstractFontConverter::importGlyphCacheFromFile(): cannot open file" << filename; + return nullptr; + } + + /* Create array to hold file contents */ + in.seekg(0, std::ios::end); + Containers::Array data(in.tellg()); + + /* Read data, close */ + in.seekg(0, std::ios::beg); + in.read(reinterpret_cast(data.begin()), data.size()); + in.close(); + + return doImportGlyphCacheFromSingleData(data); +} + +}} diff --git a/src/Text/AbstractFontConverter.h b/src/Text/AbstractFontConverter.h new file mode 100644 index 000000000..97ff78d08 --- /dev/null +++ b/src/Text/AbstractFontConverter.h @@ -0,0 +1,313 @@ +#ifndef Magnum_Text_AbstractFontConverter_h +#define Magnum_Text_AbstractFontConverter_h +/* + 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. +*/ + +/** @file + * @brief Class Magnum::Text::AbstractFontConverter + */ + +#include + +#include "Magnum.h" +#include "Text/Text.h" +#include "Text/magnumTextVisibility.h" + +namespace Magnum { namespace Text { + +/** +@brief Base for font converter plugins + +Provides functionality for converting arbitrary font to different format. + +@section AbstractFontConverter-subclassing Subclassing + +Plugin implements doFeatures() and one or more of `exportTo*()` / `importFrom*()` +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 `doExportFontTo*()` are called only if @ref Feature "Feature::ExportFont" + is supported, functions `doExportGlyphCacheTo*()` are called only if + @ref Feature "Feature::ExportGlyphCache" is supported. +- Functions `doImportGlyphCacheFrom*()` are called only if + @ref Feature "Feature::ImportGlyphCache" is supported. +- Functions `doExport*To*Data()` and `doImport*From*Data()` are called only + if @ref Feature "Feature::ConvertData" is supported. +- Function `doImport*FromData()` is called only if there is at least one data + array passed. +*/ +class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPlugin { + CORRADE_PLUGIN_INTERFACE("cz.mosra.magnum.Text.AbstractFontConverter/0.1") + + public: + /** + * @brief Features supported by this converter + * + * @see Features, features() + */ + enum class Feature: UnsignedByte { + /** + * Exporting font using exportToFile() or exportToData() + * @see @ref Feature "Feature::ConvertData" + */ + ExportFont = 1 << 0, + + /** + * Export glyph cache using exportToFile() or exportToData() + * @see @ref Feature "Feature::ConvertData" + */ + ExportGlyphCache = 1 << 1, + + /** + * Import glyph cache using importFromFile() or importFromData() + * @see @ref Feature "Feature::ConvertData" + */ + ImportGlyphCache = 1 << 2, + + /** Convert from/to data using exportToData() or importFromData() */ + ConvertData = 1 << 4, + + /** + * The format is multi-file, thus exportToSingleData() and + * importFromSingleData() convenience functions cannot be used. + */ + MultiFile = 1 << 5 + }; + + /** + * @brief Features supported by this converter + * + * @see features() + */ + typedef Containers::EnumSet Features; + + /** @brief Default constructor */ + explicit AbstractFontConverter(); + + /** @brief Plugin manager constructor */ + explicit AbstractFontConverter(PluginManager::AbstractManager* manager, std::string plugin); + + /** @brief Features supported by this converter */ + Features features() const { return doFeatures(); } + + /** + * @brief Export font to raw data + * @param font Opened font + * @param cache Populated glyph cache + * @param filename Output filename + * @param characters Characters to export + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportFont" is supported. Returns pairs of + * filename and data on success, empty vector otherwise. All data will + * be sharing common basename derived from @p filename. If the plugin + * doesn't have @ref Feature "Feature::MultiFile", only one pair is + * returned, thus using exportFontToSingleData() might be more convenient + * in that case. + * @see features(), exportFontToFile(), exportGlyphCacheToData() + */ + std::vector>> exportFontToData(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::string& characters) const; + + /** + * @brief Export font to single raw data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportFont" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile". Returns data on + * success, zero-sized array otherwise. See exportFontToData() for + * more information. + * @see features(), exportFontToFile(), importFromSingleData() + */ + Containers::Array exportFontToSingleData(AbstractFont* font, GlyphCache* cache, const std::string& characters) const; + + /** + * @brief Export font to file + * + * Available only if @ref Feature "Feature::ExportFont" is supported. + * If the plugin has @ref Feature "Feature::MultiFile", the function + * will create more than one file in given path, all sharing common + * basename derived from @p filename. Returns `true` on success, + * `false` otherwise. See exportFontToData() for more information. + * @see features(), exportFontToData(), exportGlyphCacheToFile() + */ + bool exportFontToFile(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::string& characters) const; + + /** + * @brief Export glyph cache to raw data + * @param cache Populated glyph cache + * @param filename Output filename + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportGlyphCache" is supported. Returns pairs + * of filename and data on success, empty vector otherwise. All data + * will be sharing common basename derived from @p filename. If the + * plugin doesn't have @ref Feature "Feature::MultiFile", only one pair + * is returned, thus using exportGlyphCacheToSingleData() might be more + * convenient in that case. + * + * All glyphs from given cache will be exported. If you want to export + * smaller subset, fill the cache with less characters. + * @see features(), exportGlyphCacheToFile(), exportFontToData() + */ + std::vector>> exportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const; + + /** + * @brief Export glyph cache to single raw data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ExportGlyphCache" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile". Returns data on + * success, zero-sized array otherwise. See exportGlyphCacheToData() + * for more information. + * @see features(), exportGlyphCacheToFile(), importGlyphCacheFromSingleData() + */ + Containers::Array exportGlyphCacheToSingleData(GlyphCache* cache) const; + + /** + * @brief Export glyph cache to file + * + * Available only if @ref Feature "Feature::ExportGlyphCache" is + * supported. If the plugin has @ref Feature "Feature::MultiFile", the + * function will create more than one file in given path, all sharing + * common basename derived from @p filename. Returns `true` on success, + * `false` otherwise. + * @see features(), exportGlyphCacheToData(), exportFontToFile() + */ + bool exportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const; + + /** + * @brief Import glyph cache from raw data + * @param data Pairs of filename and file data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ImportGlyphCache" is supported. Returns + * imported cache on success, `nullptr` otherwise. If the plugin + * doesn't have @ref Feature "Feature::MultiFile", only one file is + * needed, thus using convertToSingleData() might be more convenient in + * that case. + * @see features(), importFromFile(), exportToData() + */ + GlyphCache* importGlyphCacheFromData(const std::vector>>& data) const; + + /** + * @brief Import glyph cache from single raw data + * + * Available only if @ref Feature "Feature::ConvertData" and + * @ref Feature "Feature::ImportGlyphCache" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile". Returns imported + * cache on success, `nullptr` otherwise. See importFromData() for + * multi-file conversion. + * @see features(), importFromFile(), exportToSingleData() + */ + GlyphCache* importGlyphCacheFromSingleData(Containers::ArrayReference data) const; + + /** + * @brief Import glyph cache from file + * + * Available only if @ref Feature "Feature::ImportGlyphCache" is + * supported. If the plugin has @ref Feature "Feature::MultiFile", the + * function will use additional files in given path, all sharing common + * basename derived from @p filename. Returns imported cache on + * success, `nullptr` otherwise. + * @see features(), importFromData(), exportToFile() + */ + GlyphCache* importGlyphCacheFromFile(const std::string& filename) const; + + #ifndef DOXYGEN_GENERATING_OUTPUT + private: + #else + protected: + #endif + /** @brief Implementation for features() */ + virtual Features doFeatures() const = 0; + + /** + * @brief Implementation for exportFontToData() + * + * If the plugin doesn't have @ref Feature "Feature::MultiFile", + * default implementation calls doExportFontToSingleData(). + */ + virtual std::vector>> doExportFontToData(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::u32string& characters) const; + + /** @brief Implementation for exportFontToSingleData() */ + virtual Containers::Array doExportFontToSingleData(AbstractFont* font, GlyphCache* cache, const std::u32string& characters) const; + + /** + * @brief Implementation for exportFontToFile() + * + * If @ref Feature "Feature::ConvertData" is supported, default + * implementation calls doExportFontToData() and saves the result to + * given file(s). + */ + virtual bool doExportFontToFile(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::u32string& characters) const; + + /** + * @brief Implementation for exportGlyphCacheToData() + * + * If the plugin doesn't have @ref Feature "Feature::MultiFile", + * default implementation calls doExportGlyphCacheToSingleData(). + */ + virtual std::vector>> doExportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const; + + /** @brief Implementation for exportGlyphCacheToSingleData() */ + virtual Containers::Array doExportGlyphCacheToSingleData(GlyphCache* cache) const; + + /** + * @brief Implementation for exportGlyphCacheToFile() + * + * If @ref Feature "Feature::ConvertData" is supported, default + * implementation calls doExportGlyphCacheToData() and saves the result + * to given file(s). + */ + virtual bool doExportGlyphCacheToFile(GlyphCache* cache, const std::string& filename) const; + + /** + * @brief Implementation for importGlyphCacheFromData() + * + * If the plugin doesn't have @ref Feature "Feature::MultiFile", + * default implementation calls doImportGlyphCacheFromSingleData(). + */ + virtual GlyphCache* doImportGlyphCacheFromData(const std::vector>>& data) const; + + /** @brief Implementation for importGlyphCacheFromSingleData() */ + virtual GlyphCache* doImportGlyphCacheFromSingleData(Containers::ArrayReference data) const; + + /** + * @brief Implementation for importGlyphCacheFromFile() + * + * If @ref Feature "Feature::ConvertData" is supported and the plugin + * doesn't have @ref Feature "Feature::MultiFile", default + * implementation opens the file and calls doImportGlyphCacheFromSingleData() + * with its contents. + */ + virtual GlyphCache* doImportGlyphCacheFromFile(const std::string& filename) const; +}; + +CORRADE_ENUMSET_OPERATORS(AbstractFontConverter::Features) + +}} + +#endif diff --git a/src/Text/CMakeLists.txt b/src/Text/CMakeLists.txt index b7e7f30a2..daed39f76 100644 --- a/src/Text/CMakeLists.txt +++ b/src/Text/CMakeLists.txt @@ -24,11 +24,13 @@ set(MagnumText_SRCS AbstractFont.cpp + AbstractFontConverter.cpp DistanceFieldGlyphCache.cpp GlyphCache.cpp TextRenderer.cpp) set(MagnumText_HEADERS AbstractFont.h + AbstractFontConverter.h DistanceFieldGlyphCache.h GlyphCache.h Text.h diff --git a/src/Text/Test/AbstractFontConverterTest.cpp b/src/Text/Test/AbstractFontConverterTest.cpp new file mode 100644 index 000000000..b5c9cf380 --- /dev/null +++ b/src/Text/Test/AbstractFontConverterTest.cpp @@ -0,0 +1,202 @@ +/* + 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 "Text/AbstractFontConverter.h" + +#include "testConfigure.h" + +namespace Magnum { namespace Text { namespace Test { + +class AbstractFontConverterTest: public TestSuite::Tester { + public: + explicit AbstractFontConverterTest(); + + void exportFontToSingleData(); + void exportFontToFile(); + + void exportGlyphCacheToSingleData(); + void exportGlyphCacheToFile(); + + void importGlyphCacheFromSingleData(); + void importGlyphCacheFromFile(); +}; + +AbstractFontConverterTest::AbstractFontConverterTest() { + addTests({&AbstractFontConverterTest::exportFontToSingleData, + &AbstractFontConverterTest::exportFontToFile, + + &AbstractFontConverterTest::exportGlyphCacheToSingleData, + &AbstractFontConverterTest::exportGlyphCacheToFile, + + &AbstractFontConverterTest::importGlyphCacheFromSingleData, + &AbstractFontConverterTest::importGlyphCacheFromFile}); +} + +void AbstractFontConverterTest::exportFontToSingleData() { + class SingleDataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont; } + + Containers::Array doExportFontToSingleData(AbstractFont*, GlyphCache*, const std::u32string&) const override { + Containers::Array data(1); + data[0] = 0xee; + return std::move(data); + } + }; + + /* doExportFontToData() should call doExportFontToSingleData() */ + SingleDataExporter exporter; + auto ret = exporter.exportFontToData(nullptr, nullptr, "font.out", {}); + CORRADE_COMPARE(ret.size(), 1); + CORRADE_COMPARE(ret[0].first, "font.out"); + CORRADE_COMPARE(ret[0].second.size(), 1); + CORRADE_COMPARE(ret[0].second[0], 0xee); +} + +void AbstractFontConverterTest::exportFontToFile() { + class DataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont|Feature::MultiFile; } + + std::vector>> doExportFontToData(AbstractFont*, GlyphCache*, const std::string& filename, const std::u32string&) const override { + Containers::Array file(1); + file[0] = 0xf0; + + Containers::Array data(2); + data[0] = 0xfe; + data[1] = 0xed; + + std::vector>> out; + out.emplace_back(filename, std::move(file)); + out.emplace_back(filename + ".data", std::move(data)); + return std::move(out); + } + }; + + /* Remove previous files */ + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out")); + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out.data")); + + /* doExportToFile() should call doExportToData() */ + DataExporter exporter; + bool exported = exporter.exportFontToFile(nullptr, nullptr, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out"), {}); + CORRADE_VERIFY(exported); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out"), + "\xf0", TestSuite::Compare::FileToString); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "font.out.data"), + "\xfe\xed", TestSuite::Compare::FileToString); +} + +void AbstractFontConverterTest::exportGlyphCacheToSingleData() { + class SingleDataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportGlyphCache; } + + Containers::Array doExportGlyphCacheToSingleData(GlyphCache*) const override { + Containers::Array data(1); + data[0] = 0xee; + return std::move(data); + } + }; + + /* doExportGlyphCacheToData() should call doExportGlyphCacheToSingleData() */ + SingleDataExporter exporter; + auto ret = exporter.exportGlyphCacheToData(nullptr, "font.out"); + CORRADE_COMPARE(ret.size(), 1); + CORRADE_COMPARE(ret[0].first, "font.out"); + CORRADE_COMPARE(ret[0].second.size(), 1); + CORRADE_COMPARE(ret[0].second[0], 0xee); +} + +void AbstractFontConverterTest::exportGlyphCacheToFile() { + class DataExporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportGlyphCache|Feature::MultiFile; } + + std::vector>> doExportGlyphCacheToData(GlyphCache*, const std::string& filename) const override { + Containers::Array file(1); + file[0] = 0xf0; + + Containers::Array data(2); + data[0] = 0xfe; + data[1] = 0xed; + + std::vector>> out; + out.emplace_back(filename, std::move(file)); + out.emplace_back(filename + ".data", std::move(data)); + return std::move(out); + } + }; + + /* Remove previous files */ + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out")); + Utility::Directory::rm(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out.data")); + + /* doExportGlyphCacheToFile() should call doExportGlyphCacheToData() */ + DataExporter exporter; + bool exported = exporter.exportGlyphCacheToFile(nullptr, Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out")); + CORRADE_VERIFY(exported); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out"), + "\xf0", TestSuite::Compare::FileToString); + CORRADE_COMPARE_AS(Utility::Directory::join(TEXT_TEST_OUTPUT_DIR, "glyphcache.out.data"), + "\xfe\xed", TestSuite::Compare::FileToString); +} + +namespace { + +class SingleGlyphCacheDataImporter: public Text::AbstractFontConverter { + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ImportGlyphCache; } + + GlyphCache* doImportGlyphCacheFromSingleData(const Containers::ArrayReference data) const override { + if(data.size() == 1 && data[0] == 0xa5) return reinterpret_cast(0xdeadbeef); + return nullptr; + } +}; + +} + +void AbstractFontConverterTest::importGlyphCacheFromSingleData() { + /* doImportFromData() should call doImportFromSingleData() */ + SingleGlyphCacheDataImporter importer; + const unsigned char data[] = {0xa5}; + GlyphCache* cache = importer.importGlyphCacheFromData({{{}, data}}); + CORRADE_COMPARE(cache, reinterpret_cast(0xdeadbeef)); +} + +void AbstractFontConverterTest::importGlyphCacheFromFile() { + /* doImportFromFile() should call doImportFromSingleData() */ + SingleGlyphCacheDataImporter importer; + GlyphCache* cache = importer.importGlyphCacheFromFile(Utility::Directory::join(TEXT_TEST_DIR, "data.bin")); + CORRADE_COMPARE(cache, reinterpret_cast(0xdeadbeef)); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractFontConverterTest) diff --git a/src/Text/Test/CMakeLists.txt b/src/Text/Test/CMakeLists.txt index e190c7785..ad0affe81 100644 --- a/src/Text/Test/CMakeLists.txt +++ b/src/Text/Test/CMakeLists.txt @@ -28,3 +28,4 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/testConfigure.h.cmake include_directories(${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(TextAbstractFontTest AbstractFontTest.cpp LIBRARIES Magnum MagnumText) +corrade_add_test(TextAbstractFontConverterTest AbstractFontConverterTest.cpp LIBRARIES Magnum MagnumText) diff --git a/src/Text/Test/testConfigure.h.cmake b/src/Text/Test/testConfigure.h.cmake index 0cfeafcbe..62d5a8037 100644 --- a/src/Text/Test/testConfigure.h.cmake +++ b/src/Text/Test/testConfigure.h.cmake @@ -23,3 +23,4 @@ */ #define TEXT_TEST_DIR "${CMAKE_CURRENT_SOURCE_DIR}" +#define TEXT_TEST_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}" diff --git a/src/Text/Text.h b/src/Text/Text.h index aae3a8ea5..e0b51c2cf 100644 --- a/src/Text/Text.h +++ b/src/Text/Text.h @@ -35,6 +35,7 @@ namespace Magnum { namespace Text { class AbstractFont; +class AbstractFontConverter; class AbstractLayouter; class DistanceFieldGlyphCache; class GlyphCache; From 60d4f8066a584f4bf94db42424d527b79b97bb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 23 Jun 2013 17:22:08 +0200 Subject: [PATCH 23/36] Text: using utility function instead of own (bugged) implementation. Also removed the test, as there is now nothing to test. --- src/Text/AbstractFont.cpp | 11 +---------- src/Text/Test/AbstractFontTest.cpp | 30 +----------------------------- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/src/Text/AbstractFont.cpp b/src/Text/AbstractFont.cpp index 741d1b48c..ba2039a88 100644 --- a/src/Text/AbstractFont.cpp +++ b/src/Text/AbstractFont.cpp @@ -106,16 +106,7 @@ void AbstractFont::close() { void AbstractFont::createGlyphCache(GlyphCache* const cache, const std::string& characters) { CORRADE_ASSERT(isOpened(), "Text::AbstractFont::createGlyphCache(): no font opened", ); - /* Get glyph codes from characters */ - std::u32string unicodeCharacters; - unicodeCharacters.reserve(characters.size()+1); - for(std::size_t i = 0; i != characters.size(); ) { - char32_t unicode; - std::tie(unicode, i) = Utility::Unicode::nextChar(characters, i); - unicodeCharacters.push_back(unicode); - } - - doCreateGlyphCache(cache, unicodeCharacters); + doCreateGlyphCache(cache, Utility::Unicode::utf32(characters)); } AbstractLayouter* AbstractFont::layout(const GlyphCache* const cache, const Float size, const std::string& text) { diff --git a/src/Text/Test/AbstractFontTest.cpp b/src/Text/Test/AbstractFontTest.cpp index 4447b9aaa..ae0c147e9 100644 --- a/src/Text/Test/AbstractFontTest.cpp +++ b/src/Text/Test/AbstractFontTest.cpp @@ -38,15 +38,11 @@ class AbstractFontTest: public TestSuite::Tester { void openSingleData(); void openFile(); - - void createGlyphCache(); }; AbstractFontTest::AbstractFontTest() { addTests({&AbstractFontTest::openSingleData, - &AbstractFontTest::openFile, - - &AbstractFontTest::createGlyphCache}); + &AbstractFontTest::openFile}); } namespace { @@ -89,30 +85,6 @@ void AbstractFontTest::openFile() { CORRADE_VERIFY(font.isOpened()); } -void AbstractFontTest::createGlyphCache() { - class CachingFont: public Text::AbstractFont { - public: - std::u32string cacheCharacters; - - private: - Features doFeatures() const override { return {}; } - bool doIsOpened() const override { return true; } - void doClose() override {} - - void doCreateGlyphCache(GlyphCache*, const std::u32string& characters) override { - cacheCharacters = characters; - } - - AbstractLayouter* doLayout(const GlyphCache*, Float, const std::string&) override { - return nullptr; - } - }; - - CachingFont font; - font.createGlyphCache(nullptr, "žluťoučký kůň"); - CORRADE_COMPARE(font.cacheCharacters, U"\u017Elu\u0165ou\u010Dk\u00FD k\u016F\u0148"); -} - }}} CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractFontTest) From 61377a931e73d560ae04d5747897fe3223a6bea5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 23 Jun 2013 17:23:47 +0200 Subject: [PATCH 24/36] Text: ability to create glyph cache from all characters. --- src/Text/AbstractFont.cpp | 2 ++ src/Text/AbstractFont.h | 12 ++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Text/AbstractFont.cpp b/src/Text/AbstractFont.cpp index ba2039a88..aad5e564a 100644 --- a/src/Text/AbstractFont.cpp +++ b/src/Text/AbstractFont.cpp @@ -105,6 +105,8 @@ void AbstractFont::close() { void AbstractFont::createGlyphCache(GlyphCache* const cache, const std::string& characters) { CORRADE_ASSERT(isOpened(), "Text::AbstractFont::createGlyphCache(): no font opened", ); + CORRADE_ASSERT(!characters.empty() || features() & Feature::Enumerable, + "Text::AbstractFont::createGlyphCache(): the font is not enumerable, can't create cache from all characters", ); doCreateGlyphCache(cache, Utility::Unicode::utf32(characters)); } diff --git a/src/Text/AbstractFont.h b/src/Text/AbstractFont.h index de2f4cf45..6b1450bc2 100644 --- a/src/Text/AbstractFont.h +++ b/src/Text/AbstractFont.h @@ -81,7 +81,13 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * The format is multi-file, thus openSingleData() convenience * function cannot be used. */ - MultiFile = 1 << 1 + MultiFile = 1 << 1, + + /** + * The font is enumerable, i.e. it is possible to loop over all + * characters in the font. + */ + Enumerable = 1 << 2 }; /** @brief Set of features supported by this importer */ @@ -146,7 +152,9 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * @param cache Glyph cache instance * @param characters UTF-8 characters to render * - * Fills the cache with given characters. + * Fills the cache with given characters. If @ref Feature "Feature::Enumerable" + * is supported, @p characters can be empty and all glyphs from given + * font will be added to the cache. */ void createGlyphCache(GlyphCache* cache, const std::string& characters); From 10e8744ddf996842214cf77b2b173ee64c45d056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 Jun 2013 20:16:19 +0200 Subject: [PATCH 25/36] Text: ability to iterate over GlyphCache. --- src/Text/GlyphCache.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Text/GlyphCache.h b/src/Text/GlyphCache.h index 39dacc9c0..f88ca2318 100644 --- a/src/Text/GlyphCache.h +++ b/src/Text/GlyphCache.h @@ -105,6 +105,16 @@ class MAGNUM_TEXT_EXPORT GlyphCache { return it == glyphs.end() ? glyphs.at(0) : it->second; } + /** @brief Iterator access to cache data */ + std::unordered_map>::const_iterator begin() const { + return glyphs.begin(); + } + + /** @brief Iterator access to cache data */ + std::unordered_map>::const_iterator end() const { + return glyphs.end(); + } + /** * @brief Layout glyphs with given sizes to the cache * From 8bce85b308b110750325bf7130c51dae120eed39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 26 Jun 2013 19:52:12 +0200 Subject: [PATCH 26/36] Text: added AbstractFont::glyph{Id,Advance}() accessors. --- src/Text/AbstractFont.cpp | 12 ++++++++++++ src/Text/AbstractFont.h | 24 ++++++++++++++++++++++++ src/Text/Test/AbstractFontTest.cpp | 4 ++++ 3 files changed, 40 insertions(+) diff --git a/src/Text/AbstractFont.cpp b/src/Text/AbstractFont.cpp index aad5e564a..c677e354e 100644 --- a/src/Text/AbstractFont.cpp +++ b/src/Text/AbstractFont.cpp @@ -103,6 +103,18 @@ void AbstractFont::close() { if(isOpened()) doClose(); } +UnsignedInt AbstractFont::glyphId(const char32_t character) { + CORRADE_ASSERT(isOpened(), "Text::AbstractFont::glyphId(): no font opened", 0); + + return doGlyphId(character); +} + +Vector2 AbstractFont::glyphAdvance(const UnsignedInt glyph) { + CORRADE_ASSERT(isOpened(), "Text::AbstractFont::glyphAdvance(): no font opened", {}); + + return doGlyphAdvance(glyph); +} + void AbstractFont::createGlyphCache(GlyphCache* const cache, const std::string& characters) { CORRADE_ASSERT(isOpened(), "Text::AbstractFont::createGlyphCache(): no font opened", ); CORRADE_ASSERT(!characters.empty() || features() & Feature::Enumerable, diff --git a/src/Text/AbstractFont.h b/src/Text/AbstractFont.h index 6b1450bc2..fb5bac9d5 100644 --- a/src/Text/AbstractFont.h +++ b/src/Text/AbstractFont.h @@ -147,6 +147,24 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { /** @brief Font size */ Float size() const { return _size; } + /** + * @brief Glyph ID for given character + * + * @note This function is not meant to be used in performance-critical + * code, only for font observations and conversions. + */ + UnsignedInt glyphId(char32_t character); + + /** + * @brief Glyph advance + * @param glyph Glyph ID + * + * @note This function is not meant to be used in performance-critical + * code, only for font observations and conversions. + * @see glyphId() + */ + Vector2 glyphAdvance(UnsignedInt glyph); + /** * @brief Create glyph cache for given character set * @param cache Glyph cache instance @@ -210,6 +228,12 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { /** @brief Implementation for close() */ virtual void doClose() = 0; + /** @brief Implementation for glyphId() */ + virtual UnsignedInt doGlyphId(char32_t character) = 0; + + /** @brief Implementation for glyphAdvance() */ + virtual Vector2 doGlyphAdvance(UnsignedInt glyph) = 0; + /** * @brief Implementation for createGlyphCache() * diff --git a/src/Text/Test/AbstractFontTest.cpp b/src/Text/Test/AbstractFontTest.cpp index ae0c147e9..6d3541c0f 100644 --- a/src/Text/Test/AbstractFontTest.cpp +++ b/src/Text/Test/AbstractFontTest.cpp @@ -59,6 +59,10 @@ class SingleDataFont: public Text::AbstractFont { void doCreateGlyphCache(GlyphCache*, const std::u32string&) override {} + UnsignedInt doGlyphId(char32_t) override { return 0; } + + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + AbstractLayouter* doLayout(const GlyphCache*, Float, const std::string&) { return nullptr; } From 8cb4243f149ec4aabbb83c2f0ea525bbbf4018e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 01:45:45 +0200 Subject: [PATCH 27/36] Text: forgotten EnumSet operators. --- src/Text/AbstractFont.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Text/AbstractFont.h b/src/Text/AbstractFont.h index fb5bac9d5..30e57cafc 100644 --- a/src/Text/AbstractFont.h +++ b/src/Text/AbstractFont.h @@ -246,6 +246,8 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { virtual AbstractLayouter* doLayout(const GlyphCache* cache, Float size, const std::string& text) = 0; }; +CORRADE_ENUMSET_OPERATORS(AbstractFont::Features) + /** @brief Base for text layouters From 729484f6de27f719684fbf336ac1abc0f47c6c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 14:23:49 +0200 Subject: [PATCH 28/36] Text: implicitly add "Not Found" glyph to GlyphCache. Also added test for GlyphCache functionality. --- src/Text/GlyphCache.cpp | 11 +++- src/Text/GlyphCache.h | 14 +++-- src/Text/Test/CMakeLists.txt | 4 ++ src/Text/Test/GlyphCacheGLTest.cpp | 83 ++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 src/Text/Test/GlyphCacheGLTest.cpp diff --git a/src/Text/GlyphCache.cpp b/src/Text/GlyphCache.cpp index 115e1ece6..cb99aa47a 100644 --- a/src/Text/GlyphCache.cpp +++ b/src/Text/GlyphCache.cpp @@ -55,12 +55,15 @@ GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& padding): _size(siz GlyphCache::~GlyphCache() = default; -/** @todo Delegating constructor when support for GCC 4.6 is dropped */ void GlyphCache::initialize(const TextureFormat internalFormat, const Vector2i& size) { + /* Initialize texture */ _texture.setWrapping(Sampler::Wrapping::ClampToEdge) ->setMinificationFilter(Sampler::Filter::Linear) ->setMagnificationFilter(Sampler::Filter::Linear) ->setStorage(1, internalFormat, size); + + /* Default "Not Found" glyph */ + glyphs.insert({0, {}}); } std::vector GlyphCache::reserve(const std::vector& sizes) { @@ -74,7 +77,11 @@ void GlyphCache::insert(const UnsignedInt glyph, Vector2i position, Rectanglei r rectangle.bottomLeft() -= _padding; rectangle.topRight() += _padding; - glyphs.insert({glyph, {position, rectangle}}); + /* Overwriting "Not Found" glyph */ + if(glyph == 0) glyphs[0] = {position, rectangle}; + + /* Inserting new glyph */ + else CORRADE_INTERNAL_ASSERT_OUTPUT(glyphs.insert({glyph, {position, rectangle}}).second); } void GlyphCache::setImage(const Vector2i& offset, const ImageReference2D& image) { diff --git a/src/Text/GlyphCache.h b/src/Text/GlyphCache.h index f88ca2318..527617f15 100644 --- a/src/Text/GlyphCache.h +++ b/src/Text/GlyphCache.h @@ -97,8 +97,11 @@ class MAGNUM_TEXT_EXPORT GlyphCache { * @param glyph Glyph ID * * First tuple element is glyph position relative to point on baseline, - * second element is glyph region in texture atlas. If no glyph is - * found, glyph on zero index is returned. + * second element is glyph region in texture atlas. + * + * If no glyph is found, glyph `0` is returned, which is by default on + * zero position and has zero region in texture atlas. You can reset it + * to some meaningful value in insert(). */ std::pair operator[](UnsignedInt glyph) const { auto it = glyphs.find(glyph); @@ -134,8 +137,11 @@ class MAGNUM_TEXT_EXPORT GlyphCache { * @param position Position relative to point on baseline * @param rectangle Region in texture atlas * - * You can obtain unused non-overlapping regions with reserve(). See - * also setImage() to upload glyph image. + * You can obtain unused non-overlapping regions with reserve(). You + * can't overwrite already inserted glyph, however you can reset glyph + * `0` to some meaningful value. + * + * See also setImage() to upload glyph image. */ void insert(UnsignedInt glyph, Vector2i position, Rectanglei rectangle); diff --git a/src/Text/Test/CMakeLists.txt b/src/Text/Test/CMakeLists.txt index ad0affe81..cea16a17b 100644 --- a/src/Text/Test/CMakeLists.txt +++ b/src/Text/Test/CMakeLists.txt @@ -29,3 +29,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) corrade_add_test(TextAbstractFontTest AbstractFontTest.cpp LIBRARIES Magnum MagnumText) corrade_add_test(TextAbstractFontConverterTest AbstractFontConverterTest.cpp LIBRARIES Magnum MagnumText) + +if(BUILD_GL_TESTS) + corrade_add_test(TextGlyphCacheGLTest GlyphCacheGLTest.cpp LIBRARIES MagnumText ${GL_TEST_LIBRARIES}) +endif() diff --git a/src/Text/Test/GlyphCacheGLTest.cpp b/src/Text/Test/GlyphCacheGLTest.cpp new file mode 100644 index 000000000..a46e8f583 --- /dev/null +++ b/src/Text/Test/GlyphCacheGLTest.cpp @@ -0,0 +1,83 @@ +/* + 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 "Test/AbstractOpenGLTester.h" +#include "Text/GlyphCache.h" + +namespace Magnum { namespace Text { namespace Test { + +class GlyphCacheGLTest: public Magnum::Test::AbstractOpenGLTester { + public: + explicit GlyphCacheGLTest(); + + void initialize(); + void access(); +}; + +GlyphCacheGLTest::GlyphCacheGLTest() { + addTests({&GlyphCacheGLTest::initialize, + &GlyphCacheGLTest::access}); +} + +void GlyphCacheGLTest::initialize() { + Text::GlyphCache cache({1024, 2048}); + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(cache.texture()->imageSize(0), Vector2i(1024, 2048)); +} + +void GlyphCacheGLTest::access() { + Text::GlyphCache cache(Vector2i(236)); + Vector2i position; + Rectanglei rectangle; + + /* Default "Not Found" glyph */ + CORRADE_COMPARE(cache.glyphCount(), 1); + std::tie(position, rectangle) = cache[0]; + CORRADE_COMPARE(position, Vector2i(0, 0)); + CORRADE_COMPARE(rectangle, Rectanglei({0, 0}, {0, 0})); + + /* Overwrite "Not Found" glyph */ + cache.insert(0, {3, 5}, {{10, 10}, {23, 45}}); + CORRADE_COMPARE(cache.glyphCount(), 1); + std::tie(position, rectangle) = cache[0]; + CORRADE_COMPARE(position, Vector2i(3, 5)); + CORRADE_COMPARE(rectangle, Rectanglei({10, 10}, {23, 45})); + + /* Querying available glyph */ + cache.insert(25, {3, 4}, {{15, 30}, {45, 35}}); + CORRADE_COMPARE(cache.glyphCount(), 2); + std::tie(position, rectangle) = cache[25]; + CORRADE_COMPARE(position, Vector2i(3, 4)); + CORRADE_COMPARE(rectangle, Rectanglei({15, 30}, {45, 35})); + + /* Querying not available glyph falls back to "Not Found" */ + std::tie(position, rectangle) = cache[42]; + CORRADE_COMPARE(position, Vector2i(3, 5)); + CORRADE_COMPARE(rectangle, Rectanglei({10, 10}, {23, 45})); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::GlyphCacheGLTest) From 41f4d2fa5d302d9926aa6b2e46e4a447aee890ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 18:03:14 +0200 Subject: [PATCH 29/36] Text: added tests for TextRenderer. Exposed vertex and index buffer of mutable TextRenderer, fixed computation of bounding rectangle. --- src/Text/Test/CMakeLists.txt | 1 + src/Text/Test/TextRendererGLTest.cpp | 247 +++++++++++++++++++++++++++ src/Text/TextRenderer.cpp | 69 +++++--- src/Text/TextRenderer.h | 10 +- 4 files changed, 298 insertions(+), 29 deletions(-) create mode 100644 src/Text/Test/TextRendererGLTest.cpp diff --git a/src/Text/Test/CMakeLists.txt b/src/Text/Test/CMakeLists.txt index cea16a17b..44adf1510 100644 --- a/src/Text/Test/CMakeLists.txt +++ b/src/Text/Test/CMakeLists.txt @@ -32,4 +32,5 @@ corrade_add_test(TextAbstractFontConverterTest AbstractFontConverterTest.cpp LIB if(BUILD_GL_TESTS) corrade_add_test(TextGlyphCacheGLTest GlyphCacheGLTest.cpp LIBRARIES MagnumText ${GL_TEST_LIBRARIES}) + corrade_add_test(TextRendererGLTest TextRendererGLTest.cpp LIBRARIES MagnumText ${GL_TEST_LIBRARIES}) endif() diff --git a/src/Text/Test/TextRendererGLTest.cpp b/src/Text/Test/TextRendererGLTest.cpp new file mode 100644 index 000000000..9bae32ab7 --- /dev/null +++ b/src/Text/Test/TextRendererGLTest.cpp @@ -0,0 +1,247 @@ +/* + 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 "Test/AbstractOpenGLTester.h" +#include "Text/AbstractFont.h" +#include "Text/TextRenderer.h" + +namespace Magnum { namespace Text { namespace Test { + +class TextRendererGLTest: public Magnum::Test::AbstractOpenGLTester { + public: + explicit TextRendererGLTest(); + + void renderData(); + void renderMesh(); + void mutableText(); +}; + +TextRendererGLTest::TextRendererGLTest() { + addTests({&TextRendererGLTest::renderData, + &TextRendererGLTest::renderMesh, + &TextRendererGLTest::mutableText}); +} + +namespace { + +class TestLayouter: public Text::AbstractLayouter { + public: + explicit TestLayouter(Float size, std::size_t glyphCount): _size(size) { + _glyphCount = glyphCount; + } + + std::tuple renderGlyph(const Vector2& cursorPosition, UnsignedInt i) override { + return std::make_tuple( + Rectangle(cursorPosition, cursorPosition+Vector2(3.0f, 2.0f)*((i+1)*_size)), + Rectangle({i*6.0f, 0.0f}, {(i+1)*6.0f, 10.0f}), + (Vector2::xAxis((i+1)*3.0f)+Vector2(1.0f, -1.0f))*_size + ); + } + + private: + Float _size; +}; + +class TestFont: public Text::AbstractFont { + public: + Features doFeatures() const override { return Feature::OpenData; } + + bool doIsOpened() const override { return true; } + void doClose() override {} + + UnsignedInt doGlyphId(char32_t) override { return 0; } + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + + AbstractLayouter* doLayout(const GlyphCache*, Float size, const std::string& text) override { + return new TestLayouter(size, text.size()); + } +}; + +} + +void TextRendererGLTest::renderData() { + TestFont font; + std::vector positions; + std::vector textureCoordinates; + std::vector indices; + Rectangle bounds; + std::tie(positions, textureCoordinates, indices, bounds) = Text::AbstractTextRenderer::render(&font, nullptr, 0.25f, "abc"); + + /* Three glyphs, three quads -> 12 vertices, 18 indices */ + CORRADE_COMPARE(positions.size(), 12); + CORRADE_COMPARE(textureCoordinates.size(), 12); + CORRADE_COMPARE(indices.size(), 18); + + /* Vertex positions and texture coordinates + 0---2 + | | + | | + | | + 1---3 */ + + /* Vertex positions + +---+ + +-+ | | + a |b| | c | + +-+ | | + +---+ */ + CORRADE_COMPARE(positions, (std::vector{ + {0.0f, 0.5f}, + {0.0f, 0.0f}, + {0.75f, 0.5f}, + {0.75f, 0.0f}, + + {1.0f, 0.75f}, + {1.0f, -0.25f}, + {2.5f, 0.75f}, + {2.5f, -0.25f}, + + {2.75f, 1.0f}, + {2.75f, -0.5f}, + {5.0f, 1.0f}, + {5.0f, -0.5f} + })); + + /* Texture coordinates + +-+ +-+ +-+ + |a| |b| |c| + +-+ +-+ +-+ */ + CORRADE_COMPARE(textureCoordinates, (std::vector{ + {0.0f, 10.0f}, + {0.0f, 0.0f}, + {6.0f, 10.0f}, + {6.0f, 0.0f}, + + { 6.0f, 10.0f}, + { 6.0f, 0.0f}, + {12.0f, 10.0f}, + {12.0f, 0.0f}, + + {12.0f, 10.0f}, + {12.0f, 0.0f}, + {18.0f, 10.0f}, + {18.0f, 0.0f} + })); + + /* Indices + 0---2 0---2 5 + | | | / /| + | | | / / | + | | |/ / | + 1---3 1 3---4 */ + CORRADE_COMPARE(indices, (std::vector{ + 0, 1, 2, 1, 3, 2, + 4, 5, 6, 5, 7, 6, + 8, 9, 10, 9, 11, 10 + })); + + /* Bounds */ + CORRADE_COMPARE(bounds, Rectangle({0.0f, -0.5f}, {5.0f, 1.0f})); +} + +void TextRendererGLTest::renderMesh() { + TestFont font; + Mesh mesh; + Buffer vertexBuffer, indexBuffer; + Rectangle bounds; + std::tie(mesh, bounds) = Text::TextRenderer3D::render(&font, nullptr, 0.25f, "abc", &vertexBuffer, &indexBuffer, Buffer::Usage::StaticDraw); + MAGNUM_VERIFY_NO_ERROR(); + + /* Vertex buffer contents */ + Containers::Array vertices = vertexBuffer.data(); + CORRADE_COMPARE(std::vector(vertices.begin(), vertices.end()), (std::vector{ + 0.0f, 0.5f, 0.0f, 10.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.75f, 0.5f, 6.0f, 10.0f, + 0.75f, 0.0f, 6.0f, 0.0f, + + 1.0f, 0.75f, 6.0f, 10.0f, + 1.0f, -0.25f, 6.0f, 0.0f, + 2.5f, 0.75f, 12.0f, 10.0f, + 2.5f, -0.25f, 12.0f, 0.0f, + + 2.75f, 1.0f, 12.0f, 10.0f, + 2.75f, -0.5f, 12.0f, 0.0f, + 5.0f, 1.0f, 18.0f, 10.0f, + 5.0f, -0.5f, 18.0f, 0.0f + })); + + Containers::Array indices = indexBuffer.data(); + CORRADE_COMPARE(std::vector(indices.begin(), indices.end()), (std::vector{ + 0, 1, 2, 1, 3, 2, + 4, 5, 6, 5, 7, 6, + 8, 9, 10, 9, 11, 10 + })); + + /* Bounds */ + CORRADE_COMPARE(bounds, Rectangle({0.0f, -0.5f}, {5.0f, 1.0f})); +} + +void TextRendererGLTest::mutableText() { + TestFont font; + Text::TextRenderer2D renderer(&font, nullptr, 0.25f); + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(renderer.capacity(), 0); + CORRADE_COMPARE(renderer.rectangle(), Rectangle()); + + /* Reserve some capacity */ + renderer.reserve(4, Buffer::Usage::StaticDraw, Buffer::Usage::StaticDraw); + MAGNUM_VERIFY_NO_ERROR(); + CORRADE_COMPARE(renderer.capacity(), 4); + Containers::Array indices = renderer.indexBuffer()->data(); + CORRADE_COMPARE(std::vector(indices.begin(), indices.end()), (std::vector{ + 0, 1, 2, 1, 3, 2, + 4, 5, 6, 5, 7, 6, + 8, 9, 10, 9, 11, 10, + 12, 13, 14, 13, 15, 14 + })); + + /* Render text */ + renderer.render("abc"); + MAGNUM_VERIFY_NO_ERROR(); + Containers::Array vertices = renderer.vertexBuffer()->subData(0, 48); + CORRADE_COMPARE(std::vector(vertices.begin(), vertices.end()), (std::vector{ + 0.0f, 0.5f, 0.0f, 10.0f, + 0.0f, 0.0f, 0.0f, 0.0f, + 0.75f, 0.5f, 6.0f, 10.0f, + 0.75f, 0.0f, 6.0f, 0.0f, + + 1.0f, 0.75f, 6.0f, 10.0f, + 1.0f, -0.25f, 6.0f, 0.0f, + 2.5f, 0.75f, 12.0f, 10.0f, + 2.5f, -0.25f, 12.0f, 0.0f, + + 2.75f, 1.0f, 12.0f, 10.0f, + 2.75f, -0.5f, 12.0f, 0.0f, + 5.0f, 1.0f, 18.0f, 10.0f, + 5.0f, -0.5f, 18.0f, 0.0f + })); + + /* Updated bounds */ + CORRADE_COMPARE(renderer.rectangle(), Rectangle({0.0f, -0.5f}, {5.0f, 1.0f})); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Text::Test::TextRendererGLTest) diff --git a/src/Text/TextRenderer.cpp b/src/Text/TextRenderer.cpp index 8ef22ea3a..e12ebc252 100644 --- a/src/Text/TextRenderer.cpp +++ b/src/Text/TextRenderer.cpp @@ -37,11 +37,11 @@ namespace { template void createIndices(void* output, const UnsignedInt glyphCount) { T* const out = reinterpret_cast(output); for(UnsignedInt i = 0; i != glyphCount; ++i) { - /* 0---2 2 - | / /| - | / / | - |/ / | - 1 1---3 */ + /* 0---2 0---2 5 + | | | / /| + | | | / / | + | | |/ / | + 1---3 1 3---4 */ const T vertex = i*4; const T pos = i*6; @@ -69,6 +69,9 @@ std::tuple, std::vector, std::vector, positions.reserve(vertexCount); texcoords.reserve(vertexCount); + /* Rendered rectangle */ + Rectangle rectangle; + /* Render all glyphs */ Vector2 cursorPosition; for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { @@ -77,6 +80,12 @@ std::tuple, std::vector, std::vector, Vector2 advance; std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(cursorPosition, i); + /* 0---2 + | | + | | + | | + 1---3 */ + positions.insert(positions.end(), { quadPosition.topLeft(), quadPosition.bottomLeft(), @@ -90,6 +99,10 @@ std::tuple, std::vector, std::vector, textureCoordinates.bottomRight() }); + /* Extend rectangle with current quad bounds */ + rectangle.bottomLeft() = Math::min(rectangle.bottomLeft(), quadPosition.bottomLeft()); + rectangle.topRight() = Math::max(rectangle.topRight(), quadPosition.topRight()); + /* Advance cursor position to next character */ cursorPosition += advance; } @@ -98,10 +111,6 @@ std::tuple, std::vector, std::vector, std::vector indices(layouter->glyphCount()*6); createIndices(indices.data(), layouter->glyphCount()); - /* Rendered rectangle */ - Rectangle rectangle; - if(layouter->glyphCount()) rectangle = {positions[1], positions[positions.size()-2]}; - delete layouter; return std::make_tuple(std::move(positions), std::move(texcoords), std::move(indices), rectangle); } @@ -116,6 +125,9 @@ std::tuple AbstractTextRenderer::render(AbstractFont* const fon std::vector vertices; vertices.reserve(vertexCount); + /* Rendered rectangle */ + Rectangle rectangle; + /* Render all glyphs */ Vector2 cursorPosition; for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { @@ -131,6 +143,10 @@ std::tuple AbstractTextRenderer::render(AbstractFont* const fon {quadPosition.bottomRight(), textureCoordinates.bottomRight()} }); + /* Extend rectangle with current quad bounds */ + rectangle.bottomLeft() = Math::min(rectangle.bottomLeft(), quadPosition.bottomLeft()); + rectangle.topRight() = Math::max(rectangle.topRight(), quadPosition.topRight()); + /* Advance cursor position to next character */ cursorPosition += advance; } @@ -159,10 +175,6 @@ std::tuple AbstractTextRenderer::render(AbstractFont* const fon indexBuffer->setData(indicesSize, indices, usage); delete indices; - /* Rendered rectangle */ - Rectangle rectangle; - if(layouter->glyphCount()) rectangle = {vertices[1].position, vertices[vertices.size()-2].position}; - /* Configure mesh except for vertex buffer (depends on dimension count, done in subclass) */ Mesh mesh; @@ -185,7 +197,7 @@ template std::tuple TextRenderer TextRenderer::TextRenderer(AbstractFont* const font, const GlyphCache* const cache, const Float size): AbstractTextRenderer(font, cache, size) { /* Finalize mesh configuration */ - _mesh.addInterleavedVertexBuffer(&vertexBuffer, 0, + _mesh.addInterleavedVertexBuffer(&_vertexBuffer, 0, typename Shaders::AbstractVector::Position(Shaders::AbstractVector::Position::Components::Two), typename Shaders::AbstractVector::TextureCoordinates()); } @@ -214,7 +226,7 @@ void AbstractTextRenderer::reserve(const uint32_t glyphCount, const Buffer::Usag const UnsignedInt indexCount = glyphCount*6; /* Allocate vertex buffer, reset vertex count */ - vertexBuffer.setData(vertexCount*sizeof(Vertex), nullptr, vertexBufferUsage); + _vertexBuffer.setData(vertexCount*sizeof(Vertex), nullptr, vertexBufferUsage); _mesh.setVertexCount(0); /* Allocate index buffer, reset index count and reconfigure buffer binding */ @@ -230,28 +242,32 @@ void AbstractTextRenderer::reserve(const uint32_t glyphCount, const Buffer::Usag indexType = Mesh::IndexType::UnsignedInt; indicesSize = indexCount*sizeof(UnsignedInt); } - indexBuffer.setData(indicesSize, nullptr, indexBufferUsage); + _indexBuffer.setData(indicesSize, nullptr, indexBufferUsage); _mesh.setIndexCount(0) - ->setIndexBuffer(&indexBuffer, 0, indexType, 0, vertexCount); + ->setIndexBuffer(&_indexBuffer, 0, indexType, 0, vertexCount); /* Prefill index buffer */ - void* indices = indexBuffer.map(0, indicesSize, Buffer::MapFlag::InvalidateBuffer|Buffer::MapFlag::Write); + void* indices = _indexBuffer.map(0, indicesSize, Buffer::MapFlag::InvalidateBuffer|Buffer::MapFlag::Write); if(vertexCount < 255) createIndices(indices, glyphCount); else if(vertexCount < 65535) createIndices(indices, glyphCount); else createIndices(indices, glyphCount); - CORRADE_INTERNAL_ASSERT_OUTPUT(indexBuffer.unmap()); + CORRADE_INTERNAL_ASSERT_OUTPUT(_indexBuffer.unmap()); } void AbstractTextRenderer::render(const std::string& text) { AbstractLayouter* layouter = font->layout(cache, size, text); - CORRADE_ASSERT(layouter->glyphCount() <= _capacity, "Text::TextRenderer::render(): capacity" << _capacity << "too small to render" << layouter->glyphCount() << "glyphs", ); + CORRADE_ASSERT(layouter->glyphCount() <= _capacity, + "Text::TextRenderer::render(): capacity" << _capacity << "too small to render" << layouter->glyphCount() << "glyphs", ); + + /* Reset rendered rectangle */ + _rectangle = {}; /* Render all glyphs */ - Vertex* const vertices = static_cast(vertexBuffer.map(0, layouter->glyphCount()*4*sizeof(Vertex), + Vertex* const vertices = static_cast(_vertexBuffer.map(0, layouter->glyphCount()*4*sizeof(Vertex), Buffer::MapFlag::InvalidateBuffer|Buffer::MapFlag::Write)); Vector2 cursorPosition; for(UnsignedInt i = 0; i != layouter->glyphCount(); ++i) { @@ -260,10 +276,9 @@ void AbstractTextRenderer::render(const std::string& text) { Vector2 advance; std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(cursorPosition, i); - if(i == 0) - _rectangle.bottomLeft() = quadPosition.bottomLeft(); - else if(i == layouter->glyphCount()-1) - _rectangle.topRight() = quadPosition.topRight(); + /* Extend rectangle with current quad bounds */ + _rectangle.bottomLeft() = Math::min(_rectangle.bottomLeft(), quadPosition.bottomLeft()); + _rectangle.topRight() = Math::max(_rectangle.topRight(), quadPosition.topRight()); const std::size_t vertex = i*4; vertices[vertex] = {quadPosition.topLeft(), textureCoordinates.topLeft()}; @@ -274,7 +289,7 @@ void AbstractTextRenderer::render(const std::string& text) { /* Advance cursor position to next character */ cursorPosition += advance; } - CORRADE_INTERNAL_ASSERT_OUTPUT(vertexBuffer.unmap()); + CORRADE_INTERNAL_ASSERT_OUTPUT(_vertexBuffer.unmap()); /* Update index count */ _mesh.setIndexCount(layouter->glyphCount()*6); diff --git a/src/Text/TextRenderer.h b/src/Text/TextRenderer.h index 4c1299a58..ee0ad3abe 100644 --- a/src/Text/TextRenderer.h +++ b/src/Text/TextRenderer.h @@ -82,7 +82,13 @@ class MAGNUM_TEXT_EXPORT AbstractTextRenderer { /** @brief Rectangle spanning the rendered text */ Rectangle rectangle() const { return _rectangle; } - /** @brief Text mesh */ + /** @brief Vertex buffer */ + Buffer* vertexBuffer() { return &_vertexBuffer; } + + /** @brief Index buffer */ + Buffer* indexBuffer() { return &_indexBuffer; } + + /** @brief Mesh */ Mesh* mesh() { return &_mesh; } /** @@ -120,7 +126,7 @@ class MAGNUM_TEXT_EXPORT AbstractTextRenderer { static std::tuple MAGNUM_LOCAL render(AbstractFont* font, const GlyphCache* cache, Float size, const std::string& text, Buffer* vertexBuffer, Buffer* indexBuffer, Buffer::Usage usage); Mesh _mesh; - Buffer vertexBuffer, indexBuffer; + Buffer _vertexBuffer, _indexBuffer; private: AbstractFont* const font; From ca6b1c9a1e3a7615a9d79ab05f8e9eb19c88166d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Tue, 2 Jul 2013 19:22:56 +0200 Subject: [PATCH 30/36] Text: removed cursorPosition from AbstractLayouter::renderGlyph(). The parameter unnecessarily complicates the implementation, as it needs to be reimplemented in _every_ plugin (and all current plugins have bug in it). --- src/Text/AbstractFont.h | 3 +-- src/Text/Test/TextRendererGLTest.cpp | 6 +++--- src/Text/TextRenderer.cpp | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/Text/AbstractFont.h b/src/Text/AbstractFont.h index 30e57cafc..402273881 100644 --- a/src/Text/AbstractFont.h +++ b/src/Text/AbstractFont.h @@ -271,12 +271,11 @@ class MAGNUM_TEXT_EXPORT AbstractLayouter { /** * @brief Render glyph * @param i Glyph index - * @param cursorPosition Cursor position * * Returns quad position, texture coordinates and advance to next * glyph. */ - virtual std::tuple renderGlyph(const Vector2& cursorPosition, UnsignedInt i) = 0; + virtual std::tuple renderGlyph(UnsignedInt i) = 0; #ifdef DOXYGEN_GENERATING_OUTPUT private: diff --git a/src/Text/Test/TextRendererGLTest.cpp b/src/Text/Test/TextRendererGLTest.cpp index 9bae32ab7..22f32a8d4 100644 --- a/src/Text/Test/TextRendererGLTest.cpp +++ b/src/Text/Test/TextRendererGLTest.cpp @@ -51,10 +51,10 @@ class TestLayouter: public Text::AbstractLayouter { _glyphCount = glyphCount; } - std::tuple renderGlyph(const Vector2& cursorPosition, UnsignedInt i) override { + std::tuple renderGlyph(UnsignedInt i) override { return std::make_tuple( - Rectangle(cursorPosition, cursorPosition+Vector2(3.0f, 2.0f)*((i+1)*_size)), - Rectangle({i*6.0f, 0.0f}, {(i+1)*6.0f, 10.0f}), + Rectangle({}, Vector2(3.0f, 2.0f)*((i+1)*_size)), + Rectangle::fromSize({i*6.0f, 0.0f}, {6.0f, 10.0f}), (Vector2::xAxis((i+1)*3.0f)+Vector2(1.0f, -1.0f))*_size ); } diff --git a/src/Text/TextRenderer.cpp b/src/Text/TextRenderer.cpp index e12ebc252..19833c636 100644 --- a/src/Text/TextRenderer.cpp +++ b/src/Text/TextRenderer.cpp @@ -78,7 +78,11 @@ std::tuple, std::vector, std::vector, /* Position of the texture in the resulting glyph, texture coordinates */ Rectangle quadPosition, textureCoordinates; Vector2 advance; - std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(cursorPosition, i); + std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(i); + + /* Move the quad to cursor */ + quadPosition.bottomLeft() += cursorPosition; + quadPosition.topRight() += cursorPosition; /* 0---2 | | @@ -134,7 +138,11 @@ std::tuple AbstractTextRenderer::render(AbstractFont* const fon /* Position of the texture in the resulting glyph, texture coordinates */ Rectangle quadPosition, textureCoordinates; Vector2 advance; - std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(cursorPosition, i); + std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(i); + + /* Move the quad to cursor */ + quadPosition.bottomLeft() += cursorPosition; + quadPosition.topRight() += cursorPosition; vertices.insert(vertices.end(), { {quadPosition.topLeft(), textureCoordinates.topLeft()}, @@ -274,7 +282,11 @@ void AbstractTextRenderer::render(const std::string& text) { /* Position of the texture in the resulting glyph, texture coordinates */ Rectangle quadPosition, textureCoordinates; Vector2 advance; - std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(cursorPosition, i); + std::tie(quadPosition, textureCoordinates, advance) = layouter->renderGlyph(i); + + /* Move the quad to cursor */ + quadPosition.bottomLeft() += cursorPosition; + quadPosition.topRight() += cursorPosition; /* Extend rectangle with current quad bounds */ _rectangle.bottomLeft() = Math::min(_rectangle.bottomLeft(), quadPosition.bottomLeft()); From 429bcdb09a443ad5748cfc2da571e49023b4bb4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 00:03:58 +0200 Subject: [PATCH 31/36] Text: publicize glyph padding in GlyphCache, simplify internals. By publicizing and documenting what "originalSize" and "padding" means whole implementation and understanding is a lot simpler. Yay! --- src/Text/DistanceFieldGlyphCache.cpp | 25 ++++++++-------- src/Text/DistanceFieldGlyphCache.h | 6 ++-- src/Text/GlyphCache.cpp | 16 ++++++----- src/Text/GlyphCache.h | 43 +++++++++++++++++++--------- 4 files changed, 54 insertions(+), 36 deletions(-) diff --git a/src/Text/DistanceFieldGlyphCache.cpp b/src/Text/DistanceFieldGlyphCache.cpp index 78ce1b833..d0643d212 100644 --- a/src/Text/DistanceFieldGlyphCache.cpp +++ b/src/Text/DistanceFieldGlyphCache.cpp @@ -34,22 +34,23 @@ namespace Magnum { namespace Text { -DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& originalSize, const Vector2i& distanceFieldSize, UnsignedInt radius): GlyphCache(originalSize, Vector2i(radius)), scale(Vector2(distanceFieldSize)/originalSize), radius(radius) { +DistanceFieldGlyphCache::DistanceFieldGlyphCache(const Vector2i& originalSize, const Vector2i& size, const UnsignedInt radius): + #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES3) + GlyphCache(TextureFormat::R8, originalSize, size, Vector2i(radius)), + #else + GlyphCache(Context::current()->isExtensionSupported() ? + TextureFormat::Red : TextureFormat::RGB, originalSize, size, Vector2i(radius)), + #endif + scale(Vector2(size)/originalSize), radius(radius) +{ #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::texture_rg); #endif - #if !defined(MAGNUM_TARGET_GLES) || defined(MAGNUM_TARGET_GLES3) - const TextureFormat internalFormat = TextureFormat::R8; - #else - const TextureFormat internalFormat = - Context::current()->isExtensionSupported() ? - TextureFormat::Red : TextureFormat::RGB; - if(internalFormat == TextureFormat::RGB) + #ifdef MAGNUM_TARGET_GLES2 + if(!Context::current()->isExtensionSupported()) Warning() << "Text::DistanceFieldGlyphCache:" << Extensions::GL::EXT::texture_rg::string() << "not supported, using inefficient RGB format for glyph cache texture"; #endif - - initialize(internalFormat, distanceFieldSize); } void DistanceFieldGlyphCache::setImage(const Vector2i& offset, const ImageReference2D& image) { @@ -81,11 +82,11 @@ void DistanceFieldGlyphCache::setImage(const Vector2i& offset, const ImageRefere ->setImage(0, internalFormat, image); /* Create distance field from input texture */ - TextureTools::distanceField(&input, &_texture, Rectanglei::fromSize(offset*scale, image.size()*scale), radius, image.size()); + TextureTools::distanceField(&input, texture(), Rectanglei::fromSize(offset*scale, image.size()*scale), radius, image.size()); } void DistanceFieldGlyphCache::setDistanceFieldImage(const Vector2i& offset, const ImageReference2D& image) { - _texture.setSubImage(0, offset, image); + texture()->setSubImage(0, offset, image); } }} diff --git a/src/Text/DistanceFieldGlyphCache.h b/src/Text/DistanceFieldGlyphCache.h index 011d11943..892f20da9 100644 --- a/src/Text/DistanceFieldGlyphCache.h +++ b/src/Text/DistanceFieldGlyphCache.h @@ -57,8 +57,8 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { public: /** * @brief Constructor - * @param originalSize Original cache texture size - * @param distanceFieldSize Size of computed distance field texture + * @param originalSize Unscaled glyph cache texture size + * @param size Actual glyph cache texture size * @param radius Distance field computation radius * * See TextureTools::distanceField() for more information about the @@ -70,7 +70,7 @@ class MAGNUM_TEXT_EXPORT DistanceFieldGlyphCache: public GlyphCache { * possible to convert the RGB texture to Luminance after it has * been rendered when blitting is not supported to save memory? */ - explicit DistanceFieldGlyphCache(const Vector2i& originalSize, const Vector2i& distanceFieldSize, UnsignedInt radius); + explicit DistanceFieldGlyphCache(const Vector2i& originalSize, const Vector2i& size, UnsignedInt radius); /** * @brief Set cache image diff --git a/src/Text/GlyphCache.cpp b/src/Text/GlyphCache.cpp index cb99aa47a..6fbfc5313 100644 --- a/src/Text/GlyphCache.cpp +++ b/src/Text/GlyphCache.cpp @@ -31,7 +31,15 @@ namespace Magnum { namespace Text { -GlyphCache::GlyphCache(const Vector2i& size): _size(size) { +GlyphCache::GlyphCache(const TextureFormat internalFormat, const Vector2i& originalSize, const Vector2i& size, const Vector2i& padding): _size(originalSize), _padding(padding) { + initialize(internalFormat, size); +} + +GlyphCache::GlyphCache(const TextureFormat internalFormat, const Vector2i& size, const Vector2i& padding): _size(size), _padding(padding) { + initialize(internalFormat, size); +} + +GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& padding): _size(size), _padding(padding) { #ifndef MAGNUM_TARGET_GLES MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::texture_rg); #endif @@ -47,12 +55,6 @@ GlyphCache::GlyphCache(const Vector2i& size): _size(size) { initialize(internalFormat, size); } -GlyphCache::GlyphCache(const Vector2i& size, const TextureFormat internalFormat): _size(size) { - initialize(internalFormat, size); -} - -GlyphCache::GlyphCache(const Vector2i& size, const Vector2i& padding): _size(size), _padding(padding) {} - GlyphCache::~GlyphCache() = default; void GlyphCache::initialize(const TextureFormat internalFormat, const Vector2i& size) { diff --git a/src/Text/GlyphCache.h b/src/Text/GlyphCache.h index 527617f15..d790abc51 100644 --- a/src/Text/GlyphCache.h +++ b/src/Text/GlyphCache.h @@ -61,21 +61,33 @@ class MAGNUM_TEXT_EXPORT GlyphCache { public: /** * @brief Constructor - * @param size Glyph cache texture size * @param internalFormat Internal texture format + * @param originalSize Unscaled glyph cache texture size + * @param size Actual glyph cache texture size + * @param padding Padding around every glyph + * + * All glyphs parameters are saved relative to @p originalSize, + * although the actual glyph cache texture has @p size. Glyph + * @p padding can be used to account for e.g. glyph shadows. + */ + explicit GlyphCache(TextureFormat internalFormat, const Vector2i& originalSize, const Vector2i& size, const Vector2i& padding); + + /** + * @brief Constructor + * + * Same as calling the above with @p originalSize and @p size the same. */ - explicit GlyphCache(const Vector2i& size, TextureFormat internalFormat); + explicit GlyphCache(TextureFormat internalFormat, const Vector2i& size, const Vector2i& padding = Vector2i()); /** * @brief Constructor - * @param size Glyph cache texture size * * Sets internal texture format to red channel only. On desktop OpenGL * requires @extension{ARB,texture_rg} (also part of OpenGL ES 3.0), in * ES2 uses @es_extension{EXT,texture_rg}, if available, or * @ref TextureFormat "TextureFormat::Luminance" as fallback. */ - explicit GlyphCache(const Vector2i& size); + explicit GlyphCache(const Vector2i& size, const Vector2i& padding = Vector2i()); virtual ~GlyphCache(); @@ -86,6 +98,9 @@ class MAGNUM_TEXT_EXPORT GlyphCache { */ Vector2i textureSize() const { return _size; } + /** @brief Glyph padding */ + Vector2i padding() const { return _padding; } + /** @brief Count of glyphs in the cache */ std::size_t glyphCount() const { return glyphs.size(); } @@ -99,9 +114,12 @@ class MAGNUM_TEXT_EXPORT GlyphCache { * First tuple element is glyph position relative to point on baseline, * second element is glyph region in texture atlas. * + * Returned values include padding. + * * If no glyph is found, glyph `0` is returned, which is by default on * zero position and has zero region in texture atlas. You can reset it * to some meaningful value in insert(). + * @see padding() */ std::pair operator[](UnsignedInt glyph) const { auto it = glyphs.find(glyph); @@ -126,8 +144,11 @@ class MAGNUM_TEXT_EXPORT GlyphCache { * was stored there, use insert() to store actual glyph on given * position and setImage() to upload glyph image. * + * Glyph @p sizes are expected to be without padding. + * * @attention Cache size must be large enough to contain all rendered * glyphs. + * @see padding() */ std::vector reserve(const std::vector& sizes); @@ -141,7 +162,10 @@ class MAGNUM_TEXT_EXPORT GlyphCache { * can't overwrite already inserted glyph, however you can reset glyph * `0` to some meaningful value. * + * Glyph parameters are expected to be without padding. + * * See also setImage() to upload glyph image. + * @see padding() */ void insert(UnsignedInt glyph, Vector2i position, Rectanglei rectangle); @@ -153,21 +177,12 @@ class MAGNUM_TEXT_EXPORT GlyphCache { */ virtual void setImage(const Vector2i& offset, const ImageReference2D& image); - #ifdef DOXYGEN_GENERATING_OUTPUT private: - #else - protected: - #endif - /* Used from DistanceFieldGlyphCache */ - explicit MAGNUM_LOCAL GlyphCache(const Vector2i& size, const Vector2i& padding); - void MAGNUM_LOCAL initialize(TextureFormat internalFormat, const Vector2i& size); - const Vector2i _size; + Vector2i _size, _padding; Texture2D _texture; - private: - const Vector2i _padding; std::unordered_map> glyphs; }; From 731aac7d74f6ffa4c846af2477ee99ec51911591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 01:56:00 +0200 Subject: [PATCH 32/36] Text: allow font to fully create its glyph cache. Some fonts are able to add individual characters to cache, some need to fully configure whole cache. --- src/Text/AbstractFont.cpp | 28 ++++++++++++++++++----- src/Text/AbstractFont.h | 36 +++++++++++++++++++++--------- src/Text/Test/AbstractFontTest.cpp | 2 -- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/Text/AbstractFont.cpp b/src/Text/AbstractFont.cpp index c677e354e..6646f47a0 100644 --- a/src/Text/AbstractFont.cpp +++ b/src/Text/AbstractFont.cpp @@ -115,12 +115,30 @@ Vector2 AbstractFont::glyphAdvance(const UnsignedInt glyph) { return doGlyphAdvance(glyph); } -void AbstractFont::createGlyphCache(GlyphCache* const cache, const std::string& characters) { - CORRADE_ASSERT(isOpened(), "Text::AbstractFont::createGlyphCache(): no font opened", ); - CORRADE_ASSERT(!characters.empty() || features() & Feature::Enumerable, - "Text::AbstractFont::createGlyphCache(): the font is not enumerable, can't create cache from all characters", ); +void AbstractFont::fillGlyphCache(GlyphCache* const cache, const std::string& characters) { + CORRADE_ASSERT(isOpened(), + "Text::AbstractFont::createGlyphCache(): no font opened", ); + CORRADE_ASSERT(!(features() & Feature::PreparedGlyphCache), + "Text::AbstractFont::fillGlyphCache(): feature not supported", ); - doCreateGlyphCache(cache, Utility::Unicode::utf32(characters)); + doFillGlyphCache(cache, Utility::Unicode::utf32(characters)); +} + +void AbstractFont::doFillGlyphCache(GlyphCache*, const std::u32string&) { + CORRADE_ASSERT(false, "Text::AbstractFont::fillGlyphCache(): feature advertised but not implemented", ); +} + +GlyphCache* AbstractFont::createGlyphCache() { + CORRADE_ASSERT(isOpened(), + "Text::AbstractFont::createGlyphCache(): no font opened", nullptr); + CORRADE_ASSERT(features() & Feature::PreparedGlyphCache, + "Text::AbstractFont::createGlyphCache(): feature not supported", nullptr); + + return doCreateGlyphCache(); +} + +GlyphCache* AbstractFont::doCreateGlyphCache() { + CORRADE_ASSERT(false, "Text::AbstractFont::createGlyphCache(): feature advertised but not implemented", nullptr); } AbstractLayouter* AbstractFont::layout(const GlyphCache* const cache, const Float size, const std::string& text) { diff --git a/src/Text/AbstractFont.h b/src/Text/AbstractFont.h index 402273881..cf6a814bd 100644 --- a/src/Text/AbstractFont.h +++ b/src/Text/AbstractFont.h @@ -84,10 +84,11 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { MultiFile = 1 << 1, /** - * The font is enumerable, i.e. it is possible to loop over all - * characters in the font. + * The font contains prepared glyph cache. + * + * @see fillGlyphCache(), createGlyphCache() */ - Enumerable = 1 << 2 + PreparedGlyphCache = 1 << 2 }; /** @brief Set of features supported by this importer */ @@ -166,15 +167,25 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { Vector2 glyphAdvance(UnsignedInt glyph); /** - * @brief Create glyph cache for given character set + * @brief Fill glyph cache with given character set * @param cache Glyph cache instance * @param characters UTF-8 characters to render * - * Fills the cache with given characters. If @ref Feature "Feature::Enumerable" - * is supported, @p characters can be empty and all glyphs from given - * font will be added to the cache. + * Fills the cache with given characters. Fonts having + * @ref Feature "Feature::PreparedGlyphCache" do not support partial + * glyph cache filling, use createGlyphCache() instead. */ - void createGlyphCache(GlyphCache* cache, const std::string& characters); + void fillGlyphCache(GlyphCache* cache, const std::string& characters); + + /** + * @brief Create glyph cache + * + * Configures and fills glyph cache with the contents of whole font. + * Available only if @ref Feature "Feature::PreparedGlyphCache" is + * supported. Other fonts support only partial glyph cache filling, + * see fillGlyphCache(). + */ + GlyphCache* createGlyphCache(); /** * @brief Layout the text using font's own layouter @@ -182,7 +193,7 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * @param size Font size * @param text %Text to layout * - * @see createGlyphCache() + * @see fillGlyphCache(), createGlyphCache() */ AbstractLayouter* layout(const GlyphCache* cache, Float size, const std::string& text); @@ -240,7 +251,12 @@ class MAGNUM_TEXT_EXPORT AbstractFont: public PluginManager::AbstractPlugin { * The string is converted from UTF-8 to UTF-32, unique characters are * *not* removed. */ - virtual void doCreateGlyphCache(GlyphCache* cache, const std::u32string& characters) = 0; + virtual void doFillGlyphCache(GlyphCache* cache, const std::u32string& characters); + + /** + * @brief Implementation for createGlyphCache() + */ + virtual GlyphCache* doCreateGlyphCache(); /** @brief Implementation for layout() */ virtual AbstractLayouter* doLayout(const GlyphCache* cache, Float size, const std::string& text) = 0; diff --git a/src/Text/Test/AbstractFontTest.cpp b/src/Text/Test/AbstractFontTest.cpp index 6d3541c0f..8a3dac3c0 100644 --- a/src/Text/Test/AbstractFontTest.cpp +++ b/src/Text/Test/AbstractFontTest.cpp @@ -57,8 +57,6 @@ class SingleDataFont: public Text::AbstractFont { opened = (data.size() == 1 && data[0] == 0xa5); } - void doCreateGlyphCache(GlyphCache*, const std::u32string&) override {} - UnsignedInt doGlyphId(char32_t) override { return 0; } Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } From 364f29e0dfdeed6a3a37a23fec1df89d3ee0d624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 02:46:01 +0200 Subject: [PATCH 33/36] Text: allow calling GlyphCache::reserve() for cache with default glyph 0. The default glyph 0 is not positioned anywhere, so it won't interfere with atlas creation. --- src/Text/GlyphCache.cpp | 3 ++- src/Text/Test/GlyphCacheGLTest.cpp | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Text/GlyphCache.cpp b/src/Text/GlyphCache.cpp index 6fbfc5313..a11338b0a 100644 --- a/src/Text/GlyphCache.cpp +++ b/src/Text/GlyphCache.cpp @@ -69,7 +69,8 @@ void GlyphCache::initialize(const TextureFormat internalFormat, const Vector2i& } std::vector GlyphCache::reserve(const std::vector& sizes) { - CORRADE_ASSERT(glyphs.empty(), "Text::GlyphCache::reserve(): reserving space in non-empty cache is not yet implemented", {}); + CORRADE_ASSERT((glyphs.size() == 1 && glyphs.at(0) == std::pair()), + "Text::GlyphCache::reserve(): reserving space in non-empty cache is not yet implemented", {}); glyphs.reserve(glyphs.size() + sizes.size()); return TextureTools::atlas(_size, sizes, _padding); } diff --git a/src/Text/Test/GlyphCacheGLTest.cpp b/src/Text/Test/GlyphCacheGLTest.cpp index a46e8f583..7578f57b5 100644 --- a/src/Text/Test/GlyphCacheGLTest.cpp +++ b/src/Text/Test/GlyphCacheGLTest.cpp @@ -33,11 +33,13 @@ class GlyphCacheGLTest: public Magnum::Test::AbstractOpenGLTester { void initialize(); void access(); + void reserve(); }; GlyphCacheGLTest::GlyphCacheGLTest() { addTests({&GlyphCacheGLTest::initialize, - &GlyphCacheGLTest::access}); + &GlyphCacheGLTest::access, + &GlyphCacheGLTest::reserve}); } void GlyphCacheGLTest::initialize() { @@ -78,6 +80,13 @@ void GlyphCacheGLTest::access() { CORRADE_COMPARE(rectangle, Rectanglei({10, 10}, {23, 45})); } +void GlyphCacheGLTest::reserve() { + Text::GlyphCache cache(Vector2i(236)); + + /* Verify that this works for "empty" cache */ + CORRADE_VERIFY(!cache.reserve({{5, 3}}).empty()); +} + }}} CORRADE_TEST_MAIN(Magnum::Text::Test::GlyphCacheGLTest) From e4b12f922c150cf9e73b3290cc4e0b454338fcfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 11:53:45 +0200 Subject: [PATCH 34/36] Text: pass unique UTF-32 characters to AbstractFontConverter functions. Removes another implementation burden from plugins, added test for this functionality. --- src/Text/AbstractFontConverter.cpp | 22 ++++++++++++---- src/Text/AbstractFontConverter.h | 6 ++++- src/Text/Test/AbstractFontConverterTest.cpp | 29 ++++++++++++++++++++- 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/Text/AbstractFontConverter.cpp b/src/Text/AbstractFontConverter.cpp index ebd8cca31..94c626c31 100644 --- a/src/Text/AbstractFontConverter.cpp +++ b/src/Text/AbstractFontConverter.cpp @@ -22,13 +22,14 @@ DEALINGS IN THE SOFTWARE. */ +#include "AbstractFontConverter.h" + +#include #include #include #include #include -#include "AbstractFontConverter.h" - namespace Magnum { namespace Text { AbstractFontConverter::AbstractFontConverter() = default; @@ -39,7 +40,7 @@ std::vector>> AbstractFo CORRADE_ASSERT(features() >= (Feature::ExportFont|Feature::ConvertData), "Text::AbstractFontConverter::exportFontToData(): feature not supported", {}); - return doExportFontToData(font, cache, filename, Utility::Unicode::utf32(characters)); + return doExportFontToData(font, cache, filename, uniqueUnicode(characters)); } std::vector>> AbstractFontConverter::doExportFontToData(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::u32string& characters) const { @@ -57,7 +58,7 @@ Containers::Array AbstractFontConverter::exportFontToSingleData(A CORRADE_ASSERT(!(features() & Feature::MultiFile), "Text::AbstractFontConverter::exportFontToSingleData(): the format is not single-file", nullptr); - return doExportFontToSingleData(font, cache, Utility::Unicode::utf32(characters)); + return doExportFontToSingleData(font, cache, uniqueUnicode(characters)); } Containers::Array AbstractFontConverter::doExportFontToSingleData(AbstractFont*, GlyphCache*, const std::u32string&) const { @@ -69,7 +70,7 @@ bool AbstractFontConverter::exportFontToFile(AbstractFont* const font, GlyphCach CORRADE_ASSERT(features() & Feature::ExportFont, "Text::AbstractFontConverter::exportFontToFile(): feature not supported", false); - return doExportFontToFile(font, cache, filename, Utility::Unicode::utf32(characters)); + return doExportFontToFile(font, cache, filename, uniqueUnicode(characters)); } bool AbstractFontConverter::doExportFontToFile(AbstractFont* const font, GlyphCache* const cache, const std::string& filename, const std::u32string& characters) const { @@ -213,4 +214,15 @@ GlyphCache* AbstractFontConverter::doImportGlyphCacheFromFile(const std::string& return doImportGlyphCacheFromSingleData(data); } +std::u32string AbstractFontConverter::uniqueUnicode(const std::string& characters) { + /* Convert UTF-8 to UTF-32 */ + std::u32string result = Utility::Unicode::utf32(characters); + + /* Remove duplicate glyphs */ + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + + return std::move(result); +} + }} diff --git a/src/Text/AbstractFontConverter.h b/src/Text/AbstractFontConverter.h index 97ff78d08..c4508a7c0 100644 --- a/src/Text/AbstractFontConverter.h +++ b/src/Text/AbstractFontConverter.h @@ -44,7 +44,8 @@ Provides functionality for converting arbitrary font to different format. @section AbstractFontConverter-subclassing Subclassing Plugin implements doFeatures() and one or more of `exportTo*()` / `importFrom*()` -functions based on what features are supported. +functions based on what features are supported. Characters passed to font +exporting functions are converted to list of unique UTF-32 characters. You don't need to do most of the redundant sanity checks, these things are checked by the implementation: @@ -304,6 +305,9 @@ class MAGNUM_TEXT_EXPORT AbstractFontConverter: public PluginManager::AbstractPl * with its contents. */ virtual GlyphCache* doImportGlyphCacheFromFile(const std::string& filename) const; + + private: + MAGNUM_TEXT_LOCAL static std::u32string uniqueUnicode(const std::string& characters); }; CORRADE_ENUMSET_OPERATORS(AbstractFontConverter::Features) diff --git a/src/Text/Test/AbstractFontConverterTest.cpp b/src/Text/Test/AbstractFontConverterTest.cpp index b5c9cf380..e27626cac 100644 --- a/src/Text/Test/AbstractFontConverterTest.cpp +++ b/src/Text/Test/AbstractFontConverterTest.cpp @@ -37,6 +37,8 @@ class AbstractFontConverterTest: public TestSuite::Tester { public: explicit AbstractFontConverterTest(); + void convertGlyphs(); + void exportFontToSingleData(); void exportFontToFile(); @@ -48,7 +50,9 @@ class AbstractFontConverterTest: public TestSuite::Tester { }; AbstractFontConverterTest::AbstractFontConverterTest() { - addTests({&AbstractFontConverterTest::exportFontToSingleData, + addTests({&AbstractFontConverterTest::convertGlyphs, + + &AbstractFontConverterTest::exportFontToSingleData, &AbstractFontConverterTest::exportFontToFile, &AbstractFontConverterTest::exportGlyphCacheToSingleData, @@ -58,6 +62,29 @@ AbstractFontConverterTest::AbstractFontConverterTest() { &AbstractFontConverterTest::importGlyphCacheFromFile}); } +void AbstractFontConverterTest::convertGlyphs() { + class GlyphExporter: public AbstractFontConverter { + public: + GlyphExporter(std::u32string& characters): characters(characters) {} + + private: + Features doFeatures() const override { return Feature::ConvertData|Feature::ExportFont; } + + Containers::Array doExportFontToSingleData(AbstractFont*, GlyphCache*, const std::u32string& characters) const override { + this->characters = characters; + return {}; + } + + std::u32string& characters; + }; + + std::u32string characters; + GlyphExporter exporter(characters); + exporter.exportFontToSingleData(nullptr, nullptr, "abC01a0 "); + CORRADE_COMPARE(characters, (std::u32string{ + U' ', U'0', U'1', U'C', U'a', U'b'})); +} + void AbstractFontConverterTest::exportFontToSingleData() { class SingleDataExporter: public Text::AbstractFontConverter { private: From 013fee8bc1dae57e208a1a9c8d8fab6ec8b8cf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 13:02:12 +0200 Subject: [PATCH 35/36] GCC 4.6 compatibility: no std::unordered_map::emplace(). Basically reverts (only for GCC 4.6) what was done in b9b690b611e2ad5ca654e78916d6ba7f676cb27a. --- src/Context.cpp | 4 ++++ src/MeshTools/RemoveDuplicates.h | 4 ++++ src/ResourceManager.h | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/src/Context.cpp b/src/Context.cpp index 7c7a76225..0a9f5e6b9 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -337,7 +337,11 @@ Context::Context() { std::unordered_map futureExtensions; for(std::size_t i = future; i != versions.size(); ++i) for(const Extension& extension: Extension::extensions(versions[i])) + #ifndef CORRADE_GCC46_COMPATIBILITY futureExtensions.emplace(extension._string, extension); + #else + futureExtensions.insert({extension._string, extension}); + #endif /* Check for presence of extensions in future versions */ #ifndef MAGNUM_TARGET_GLES2 diff --git a/src/MeshTools/RemoveDuplicates.h b/src/MeshTools/RemoveDuplicates.h index d51c7e61c..55d72e7ea 100644 --- a/src/MeshTools/RemoveDuplicates.h +++ b/src/MeshTools/RemoveDuplicates.h @@ -127,7 +127,11 @@ template void RemoveDuplicates::from(index), v); + #else + auto result = table.insert({Math::Vector::from(index), v}); + #endif *it = result.first->second.newIndex; } diff --git a/src/ResourceManager.h b/src/ResourceManager.h index 261566ff5..0831ac727 100644 --- a/src/ResourceManager.h +++ b/src/ResourceManager.h @@ -469,7 +469,11 @@ template void ResourceManagerData::set(const ResourceKey key, T* con /* Insert it, if not already here */ } else if(it == _data.end()) + #ifndef CORRADE_GCC46_COMPATIBILITY it = _data.emplace(key, Data()).first; + #else + it = _data.insert({key, Data()}).first; + #endif /* Replace previous data */ delete it->second.data; From d7d47eb639a424f59f84d619d5c9b35006425490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 3 Jul 2013 13:03:34 +0200 Subject: [PATCH 36/36] GCC 4.6 compatibility: no in-class data initializers. --- src/Text/Test/AbstractFontTest.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Text/Test/AbstractFontTest.cpp b/src/Text/Test/AbstractFontTest.cpp index 8a3dac3c0..ae5bf4932 100644 --- a/src/Text/Test/AbstractFontTest.cpp +++ b/src/Text/Test/AbstractFontTest.cpp @@ -49,6 +49,8 @@ namespace { class SingleDataFont: public Text::AbstractFont { public: + explicit SingleDataFont(): opened(false) {} + Features doFeatures() const override { return Feature::OpenData; } bool doIsOpened() const override { return opened; } void doClose() override {} @@ -65,7 +67,7 @@ class SingleDataFont: public Text::AbstractFont { return nullptr; } - bool opened = false; + bool opened; }; }