diff --git a/CMakeLists.txt b/CMakeLists.txt index 09035e936..6bae726f1 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() @@ -115,6 +116,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/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/AbstractFramebuffer.cpp b/src/AbstractFramebuffer.cpp index c410aac91..3a4ba0bb2 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(image.format(), image.type(), size, 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 b09b72387..96db29cd7 100644 --- a/src/AbstractFramebuffer.h +++ b/src/AbstractFramebuffer.h @@ -250,7 +250,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 /** @@ -264,7 +264,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 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 65784d872..423370116 100644 --- a/src/AbstractImage.h +++ b/src/AbstractImage.h @@ -42,7 +42,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 @@ -50,11 +50,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) @@ -70,16 +65,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) @@ -88,6 +80,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/AbstractTexture.cpp b/src/AbstractTexture.cpp index 07f694d2f..f404e4e78 100644 --- a/src/AbstractTexture.cpp +++ b/src/AbstractTexture.cpp @@ -953,48 +953,35 @@ 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(image.format(), image.type(), size, 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 #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; @@ -1018,6 +1005,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 018b2b228..18b64c2c6 100644 --- a/src/AbstractTexture.h +++ b/src/AbstractTexture.h @@ -100,9 +100,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 @@ -129,9 +126,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); @@ -273,8 +276,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; @@ -421,25 +424,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 }; @@ -456,13 +442,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}); @@ -491,17 +475,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}); @@ -532,17 +514,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/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/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 c0f9a965a..393c4e617 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 @@ -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/Context.cpp b/src/Context.cpp index 25014c222..dc07c1c0d 100644 --- a/src/Context.cpp +++ b/src/Context.cpp @@ -338,7 +338,11 @@ Context::Context() { for(std::size_t i = future; i != versions.size(); ++i) { const std::vector& extensions = Extension::extensions(versions[i]); for(auto it = extensions.begin(); it != extensions.end(); ++it) - futureExtensions.insert(std::make_pair(it->_string, *it)); + #ifndef CORRADE_GCC46_COMPATIBILITY + futureExtensions.emplace(it->_string, *it); + #else + futureExtensions.insert({it->_string, *it}); + #endif } /* Check for presence of extensions in future versions */ diff --git a/src/CubeMapTexture.h b/src/CubeMapTexture.h index b9f99ba88..2308568f2 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 @@ -165,33 +165,47 @@ class CubeMapTexture: public AbstractTexture { * @param coordinate Coordinate * @param level Mip level * @param internalFormat Internal format - * @param image Image, ImageWrapper, 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, ImageWrapper, 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 e4e443a2d..7219706c8 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 @@ -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) * @@ -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; } @@ -184,7 +190,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) * @@ -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, ImageWrapper2D, 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/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 54712ae18..b2da03fb2 100644 --- a/src/Image.h +++ b/src/Image.h @@ -28,16 +28,14 @@ * @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 { /** @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 */ @@ -47,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 @@ -67,9 +65,28 @@ 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; } + /** + * @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; } @@ -79,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; @@ -103,6 +120,22 @@ 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; +} + +template inline Image::operator ImageReference() const { + return ImageReference(this->format(), this->type(), _size, _data); +} + } #endif 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 76% rename from src/ImageWrapper.h rename to src/ImageReference.h index 8acbd16ac..8262a80da 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,40 +47,41 @@ 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 */ + /** * @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 ImageWrapper(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. */ - explicit ImageWrapper(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 */ - 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 @@ -100,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 87548483e..84a79ee44 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 */ @@ -392,10 +384,10 @@ enum class ImageFormat: GLenum; enum class ImageType: GLenum; #endif -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/Math/Geometry/Rectangle.h b/src/Math/Geometry/Rectangle.h index ad8ef7b20..21f3bb331 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 {{vec[0], vec[1]}, {vec[2], vec[3]}}; + } +}; + +#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 + +}} diff --git a/src/MeshTools/RemoveDuplicates.h b/src/MeshTools/RemoveDuplicates.h index c7f5e0e5e..28cb1c65f 100644 --- a/src/MeshTools/RemoveDuplicates.h +++ b/src/MeshTools/RemoveDuplicates.h @@ -129,7 +129,11 @@ template void RemoveDuplicates, HashedVertex>(Math::Vector::from(index), v)); + #ifndef CORRADE_GCC46_COMPATIBILITY + auto result = table.emplace(Math::Vector::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 cfef61a7a..e7d302f9c 100644 --- a/src/ResourceManager.h +++ b/src/ResourceManager.h @@ -471,7 +471,11 @@ 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; + #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; 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..a87a06da7 --- /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 Int data[] = {2, 7, 5, 13, 25}; + buffer.setData(5*4, data, Buffer::Usage::StaticDraw); + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(buffer.size(), 5*4); + + /** @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 Int subData[] = {125, 3, 15}; + buffer.setSubData(4, 3*4, subData); + MAGNUM_VERIFY_NO_ERROR(); + + CORRADE_COMPARE(buffer.size(), 5*4); + + /** @todo How to verify the contents in ES? */ + #ifndef MAGNUM_TARGET_GLES + const Containers::Array subContents = buffer.subData(4, 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 e9138989f..ca308059b 100644 --- a/src/Test/CMakeLists.txt +++ b/src/Test/CMakeLists.txt @@ -28,9 +28,19 @@ 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) 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) + +# 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() diff --git a/src/Test/ImageTest.cpp b/src/Test/ImageTest.cpp new file mode 100644 index 000000000..d16f3a578 --- /dev/null +++ b/src/Test/ImageTest.cpp @@ -0,0 +1,85 @@ +/* + 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(); + void toReference(); +}; + +ImageTest::ImageTest() { + addTests({&ImageTest::moveConstructor, + &ImageTest::moveAssignment, + &ImageTest::toReference}); +} + +void ImageTest::moveConstructor() { + unsigned char* data = new unsigned char[3]; + Image2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, 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(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, 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); +} + +void ImageTest::toReference() { + unsigned char* data = new unsigned char[3]; + Image2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, 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/Text/AbstractFont.cpp b/src/Text/AbstractFont.cpp index 58a90cf2b..6646f47a0 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,119 @@ 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(); +} + +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::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", ); + + 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) { + 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..cf6a814bd 100644 --- a/src/Text/AbstractFont.h +++ b/src/Text/AbstractFont.h @@ -50,63 +50,152 @@ 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, + + /** + * The font contains prepared glyph cache. + * + * @see fillGlyphCache(), createGlyphCache() + */ + PreparedGlyphCache = 1 << 2 + }; + + /** @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. Returns `true` on success, `false` otherwise. + * file. Available only if @ref Feature "Feature::OpenData" is + * supported. Returns `true` on success, `false` otherwise. */ - virtual bool open(const std::string& filename, Float size) = 0; + bool openData(const std::vector>>& data, Float size); /** - * @brief Open font from memory - * @param data Font data - * @param dataSize Font data 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 unsigned char* data, std::size_t dataSize, Float size) = 0; + bool openSingleData(Containers::ArrayReference data, Float 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. 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. + */ + 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; } /** - * @brief Create glyph cache for given character set + * @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 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. + * Fills the cache with given characters. Fonts having + * @ref Feature "Feature::PreparedGlyphCache" do not support partial + * glyph cache filling, use createGlyphCache() instead. */ - virtual void createGlyphCache(GlyphCache* cache, const std::string& characters) = 0; + void fillGlyphCache(GlyphCache* cache, const std::string& characters); /** - * @brief Layout the text using font own layouter + * @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 * @param cache Glyph cache * @param size Font size * @param text %Text to layout * - * @see createGlyphCache() + * @see fillGlyphCache(), 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,8 +203,67 @@ 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 glyphId() */ + virtual UnsignedInt doGlyphId(char32_t character) = 0; + + /** @brief Implementation for glyphAdvance() */ + virtual Vector2 doGlyphAdvance(UnsignedInt glyph) = 0; + + /** + * @brief Implementation for createGlyphCache() + * + * The string is converted from UTF-8 to UTF-32, unique characters are + * *not* removed. + */ + 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; }; +CORRADE_ENUMSET_OPERATORS(AbstractFont::Features) + /** @brief Base for text layouters @@ -139,12 +287,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/AbstractFontConverter.cpp b/src/Text/AbstractFontConverter.cpp new file mode 100644 index 000000000..94c626c31 --- /dev/null +++ b/src/Text/AbstractFontConverter.cpp @@ -0,0 +1,228 @@ +/* + 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 "AbstractFontConverter.h" + +#include +#include +#include +#include +#include + +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, uniqueUnicode(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, uniqueUnicode(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, uniqueUnicode(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); +} + +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 new file mode 100644 index 000000000..c4508a7c0 --- /dev/null +++ b/src/Text/AbstractFontConverter.h @@ -0,0 +1,317 @@ +#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. 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: + +- 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; + + private: + MAGNUM_TEXT_LOCAL static std::u32string uniqueUnicode(const std::string& characters); +}; + +CORRADE_ENUMSET_OPERATORS(AbstractFontConverter::Features) + +}} + +#endif diff --git a/src/Text/CMakeLists.txt b/src/Text/CMakeLists.txt index fa28db8d1..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 @@ -45,3 +47,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/DistanceFieldGlyphCache.cpp b/src/Text/DistanceFieldGlyphCache.cpp index 83daad155..d0643d212 100644 --- a/src/Text/DistanceFieldGlyphCache.cpp +++ b/src/Text/DistanceFieldGlyphCache.cpp @@ -25,52 +25,53 @@ #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" 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, 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,11 +82,11 @@ 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) { - _texture.setSubImage(0, offset, 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..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 @@ -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 9caf7976f..81af8c960 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,24 +55,22 @@ 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; -/** @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) { - 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", {}); #ifndef CORRADE_GCC44_COMPATIBILITY glyphs.reserve(glyphs.size() + sizes.size()); #endif @@ -76,10 +82,14 @@ 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, 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..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(const Vector2i& size, TextureFormat internalFormat); + 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(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(); } @@ -97,14 +112,30 @@ 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. + * + * 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); 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 * @@ -113,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); @@ -124,8 +158,14 @@ 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. + * + * 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); @@ -135,23 +175,14 @@ 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: - #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; }; diff --git a/src/Text/Test/AbstractFontConverterTest.cpp b/src/Text/Test/AbstractFontConverterTest.cpp new file mode 100644 index 000000000..e27626cac --- /dev/null +++ b/src/Text/Test/AbstractFontConverterTest.cpp @@ -0,0 +1,229 @@ +/* + 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 convertGlyphs(); + + void exportFontToSingleData(); + void exportFontToFile(); + + void exportGlyphCacheToSingleData(); + void exportGlyphCacheToFile(); + + void importGlyphCacheFromSingleData(); + void importGlyphCacheFromFile(); +}; + +AbstractFontConverterTest::AbstractFontConverterTest() { + addTests({&AbstractFontConverterTest::convertGlyphs, + + &AbstractFontConverterTest::exportFontToSingleData, + &AbstractFontConverterTest::exportFontToFile, + + &AbstractFontConverterTest::exportGlyphCacheToSingleData, + &AbstractFontConverterTest::exportGlyphCacheToFile, + + &AbstractFontConverterTest::importGlyphCacheFromSingleData, + &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: + 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/AbstractFontTest.cpp b/src/Text/Test/AbstractFontTest.cpp new file mode 100644 index 000000000..ae5bf4932 --- /dev/null +++ b/src/Text/Test/AbstractFontTest.cpp @@ -0,0 +1,94 @@ +/* + 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(); +}; + +AbstractFontTest::AbstractFontTest() { + addTests({&AbstractFontTest::openSingleData, + &AbstractFontTest::openFile}); +} + +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 {} + + void doOpenSingleData(const Containers::ArrayReference data, Float) override { + opened = (data.size() == 1 && data[0] == 0xa5); + } + + UnsignedInt doGlyphId(char32_t) override { return 0; } + + Vector2 doGlyphAdvance(UnsignedInt) override { return {}; } + + AbstractLayouter* doLayout(const GlyphCache*, Float, const std::string&) { + return nullptr; + } + + bool opened; +}; + +} + +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()); +} + +}}} + +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..44adf1510 --- /dev/null +++ b/src/Text/Test/CMakeLists.txt @@ -0,0 +1,36 @@ +# +# 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) +corrade_add_test(TextAbstractFontConverterTest AbstractFontConverterTest.cpp LIBRARIES Magnum MagnumText) + +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/GlyphCacheGLTest.cpp b/src/Text/Test/GlyphCacheGLTest.cpp new file mode 100644 index 000000000..7578f57b5 --- /dev/null +++ b/src/Text/Test/GlyphCacheGLTest.cpp @@ -0,0 +1,92 @@ +/* + 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(); + void reserve(); +}; + +GlyphCacheGLTest::GlyphCacheGLTest() { + addTests({&GlyphCacheGLTest::initialize, + &GlyphCacheGLTest::access, + &GlyphCacheGLTest::reserve}); +} + +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})); +} + +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) diff --git a/src/Text/Test/TextRendererGLTest.cpp b/src/Text/Test/TextRendererGLTest.cpp new file mode 100644 index 000000000..22f32a8d4 --- /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(UnsignedInt i) override { + return std::make_tuple( + 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 + ); + } + + 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/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..62d5a8037 --- /dev/null +++ b/src/Text/Test/testConfigure.h.cmake @@ -0,0 +1,26 @@ +/* + 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}" +#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; diff --git a/src/Text/TextRenderer.cpp b/src/Text/TextRenderer.cpp index 8ef22ea3a..19833c636 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,13 +69,26 @@ 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) { /* 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 + | | + | | + | | + 1---3 */ positions.insert(positions.end(), { quadPosition.topLeft(), @@ -90,6 +103,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 +115,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,13 +129,20 @@ 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) { /* 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()}, @@ -131,6 +151,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 +183,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 +205,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 +234,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,40 +250,47 @@ 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) { /* 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; - 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 +301,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; diff --git a/src/Texture.h b/src/Texture.h index 9cd269e5f..dce833157 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 @@ -346,12 +346,9 @@ template class Texture: public AbstractTexture { * @brief Set image data * @param level Mip level * @param internalFormat Internal format - * @param image Image, ImageWrapper, 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, ImageWrapper, 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 diff --git a/src/Trade/AbstractImageConverter.cpp b/src/Trade/AbstractImageConverter.cpp index ec548d374..d0a465f68 100644 --- a/src/Trade/AbstractImageConverter.cpp +++ b/src/Trade/AbstractImageConverter.cpp @@ -24,6 +24,8 @@ #include "AbstractImageConverter.h" +#include +#include #include namespace Magnum { namespace Trade { @@ -32,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); } -std::pair 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 b2826b96c..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 std::pair 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/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..606869f04 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,47 +91,35 @@ 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 - * @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 * 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 +131,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 +142,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 +150,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 +159,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 +170,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 +178,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 +187,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 +198,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 +206,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 +215,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 +226,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 +234,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 +243,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 +254,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 +262,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 +271,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 +282,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 +290,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 +299,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 +310,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 +318,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 +327,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 +338,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 +346,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 +355,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 +366,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 +374,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 +383,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 +394,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 +402,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 +411,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 +422,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 +430,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 +439,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 +450,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 +458,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 +467,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/ImageData.h b/src/Trade/ImageData.h index ab134a96a..91c4e43c6 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 { @@ -38,7 +36,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 { @@ -47,19 +45,38 @@ 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; + + /** @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; } + /** + * @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; } @@ -81,6 +98,22 @@ 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; +} + +template inline ImageData::operator ImageReference() const { + return ImageReference(this->format(), this->type(), _size, _data); +} + }} #endif diff --git a/src/Trade/Test/AbstractImageConverterTest.cpp b/src/Trade/Test/AbstractImageConverterTest.cpp new file mode 100644 index 000000000..7fbfae7c3 --- /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(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); +} + +}}} + +CORRADE_TEST_MAIN(Magnum::Trade::Test::AbstractImageConverterTest) 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..beb1c2ccf 100644 --- a/src/Trade/Test/CMakeLists.txt +++ b/src/Trade/Test/CMakeLists.txt @@ -22,5 +22,13 @@ # 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(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..76c836b1e --- /dev/null +++ b/src/Trade/Test/ImageDataTest.cpp @@ -0,0 +1,85 @@ +/* + 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(); + void toReference(); +}; + +ImageDataTest::ImageDataTest() { + addTests({&ImageDataTest::moveConstructor, + &ImageDataTest::moveAssignment, + &ImageDataTest::toReference}); +} + +void ImageDataTest::moveConstructor() { + unsigned char* data = new unsigned char[3]; + Trade::ImageData2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, 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(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, 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); +} + +void ImageDataTest::toReference() { + unsigned char* data = new unsigned char[3]; + Trade::ImageData2D a(ImageFormat::Red, ImageType::UnsignedByte, {1, 3}, 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) 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..e2882497a --- /dev/null +++ b/src/Trade/Test/testConfigure.h.cmake @@ -0,0 +1,26 @@ +/* + 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}" +#define TRADE_TEST_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}"