Browse Source

Merge branch 'master' into compatibility

Conflicts:
	src/Context.cpp
	src/Magnum.h
	src/Text/GlyphCache.cpp
Vladimír Vondruš 13 years ago
parent
commit
5e7fcd48c0
  1. 11
      CMakeLists.txt
  2. 7
      modules/FindMagnum.cmake
  3. 18
      src/AbstractFramebuffer.cpp
  4. 4
      src/AbstractFramebuffer.h
  5. 2
      src/AbstractImage.cpp
  6. 19
      src/AbstractImage.h
  7. 123
      src/AbstractTexture.cpp
  8. 78
      src/AbstractTexture.h
  9. 13
      src/Buffer.cpp
  10. 27
      src/Buffer.h
  11. 2
      src/BufferImage.h
  12. 5
      src/CMakeLists.txt
  13. 6
      src/Context.cpp
  14. 34
      src/CubeMapTexture.h
  15. 43
      src/CubeMapTextureArray.h
  16. 2
      src/Image.cpp
  17. 49
      src/Image.h
  18. 4
      src/ImageFormat.h
  19. 31
      src/ImageReference.h
  20. 18
      src/Magnum.h
  21. 30
      src/Math/Geometry/Rectangle.h
  22. 16
      src/Math/Geometry/Test/RectangleTest.cpp
  23. 38
      src/Math/Geometry/instantiation.cpp
  24. 6
      src/MeshTools/RemoveDuplicates.h
  25. 6
      src/ResourceManager.h
  26. 56
      src/Test/AbstractOpenGLTester.h
  27. 265
      src/Test/BufferGLTest.cpp
  28. 10
      src/Test/CMakeLists.txt
  29. 85
      src/Test/ImageTest.cpp
  30. 119
      src/Text/AbstractFont.cpp
  31. 187
      src/Text/AbstractFont.h
  32. 228
      src/Text/AbstractFontConverter.cpp
  33. 317
      src/Text/AbstractFontConverter.h
  34. 6
      src/Text/CMakeLists.txt
  35. 43
      src/Text/DistanceFieldGlyphCache.cpp
  36. 10
      src/Text/DistanceFieldGlyphCache.h
  37. 32
      src/Text/GlyphCache.cpp
  38. 69
      src/Text/GlyphCache.h
  39. 229
      src/Text/Test/AbstractFontConverterTest.cpp
  40. 94
      src/Text/Test/AbstractFontTest.cpp
  41. 36
      src/Text/Test/CMakeLists.txt
  42. 92
      src/Text/Test/GlyphCacheGLTest.cpp
  43. 247
      src/Text/Test/TextRendererGLTest.cpp
  44. 1
      src/Text/Test/data.bin
  45. 26
      src/Text/Test/testConfigure.h.cmake
  46. 1
      src/Text/Text.h
  47. 87
      src/Text/TextRenderer.cpp
  48. 10
      src/Text/TextRenderer.h
  49. 43
      src/Texture.h
  50. 49
      src/Trade/AbstractImageConverter.cpp
  51. 72
      src/Trade/AbstractImageConverter.h
  52. 461
      src/Trade/AbstractImporter.cpp
  53. 327
      src/Trade/AbstractImporter.h
  54. 45
      src/Trade/ImageData.h
  55. 75
      src/Trade/Test/AbstractImageConverterTest.cpp
  56. 69
      src/Trade/Test/AbstractImporterTest.cpp
  57. 8
      src/Trade/Test/CMakeLists.txt
  58. 85
      src/Trade/Test/ImageDataTest.cpp
  59. 1
      src/Trade/Test/file.bin
  60. 26
      src/Trade/Test/testConfigure.h.cmake

11
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()

7
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()

18
src/AbstractFramebuffer.cpp

@ -141,20 +141,20 @@ void AbstractFramebuffer::clear(FramebufferClearMask mask) {
glClear(static_cast<GLbitfield>(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

4
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

2
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) {

19
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

123
src/AbstractTexture.cpp

@ -953,48 +953,35 @@ void AbstractTexture::invalidateSubImageImplementationARB(GLint level, const Vec
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_TARGET_GLES
template<UnsignedInt dimensions> void AbstractTexture::image(GLenum target, GLint level, Image<dimensions>* image) {
template<UnsignedInt dimensions> void AbstractTexture::image(GLenum target, GLint level, Image<dimensions>& image) {
const Math::Vector<dimensions, Int> size = DataHelper<dimensions>::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<UnsignedInt dimensions> void AbstractTexture::image(GLenum target, GLint level, BufferImage<dimensions>* image, Buffer::Usage usage) {
template<UnsignedInt dimensions> void AbstractTexture::image(GLenum target, GLint level, BufferImage<dimensions>& image, Buffer::Usage usage) {
const Math::Vector<dimensions, Int> size = DataHelper<dimensions>::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<UnsignedInt dimensions> const GLvoid* ImageHelper<BufferImage<dimensions>>::dataOrPixelUnpackBuffer(BufferImage<dimensions>* image) {
image->buffer()->bind(Buffer::Target::PixelUnpack);
return nullptr;
}
template struct ImageHelper<BufferImage1D>;
template struct ImageHelper<BufferImage2D>;
template struct ImageHelper<BufferImage3D>;
}
#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<Sampler::Wrapping>& 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", );

78
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<UnsignedInt dimensions> void MAGNUM_LOCAL image(GLenum target, GLint level, Image<dimensions>* image);
template<UnsignedInt dimensions> void MAGNUM_LOCAL image(GLenum target, GLint level, BufferImage<dimensions>* image, Buffer::Usage usage);
template<UnsignedInt dimensions> void image(GLenum target, GLint level, Image<dimensions>& image);
template<UnsignedInt dimensions> void image(GLenum target, GLint level, BufferImage<dimensions>& image, Buffer::Usage usage);
#endif
GLenum _target;
@ -421,25 +424,8 @@ class MAGNUM_EXPORT AbstractTexture {
};
#ifndef DOXYGEN_GENERATING_OUTPUT
namespace Implementation {
template<class Image> 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<UnsignedInt dimensions> struct MAGNUM_EXPORT ImageHelper<BufferImage<dimensions>> {
static const GLvoid* dataOrPixelUnpackBuffer(BufferImage<dimensions>* 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<class Image> static typename std::enable_if<Image::Dimensions == 1, void>::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<Image>::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<class Image> static typename std::enable_if<Image::Dimensions == 1, void>::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<Image>::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<class Image> static typename std::enable_if<Image::Dimensions == 2, void>::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<Image>::dataOrPixelUnpackBuffer(image));
}
template<class Image> static typename std::enable_if<Image::Dimensions == 2, void>::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<Image>::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<class Image> static typename std::enable_if<Image::Dimensions == 1, void>::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<Image>::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<class Image> static typename std::enable_if<Image::Dimensions == 3, void>::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<Image>::dataOrPixelUnpackBuffer(image));
}
template<class Image> static typename std::enable_if<Image::Dimensions == 3, void>::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<Image>::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<class Image> static typename std::enable_if<Image::Dimensions == 2, void>::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<Image>::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);

13
src/Buffer.cpp

@ -24,7 +24,6 @@
#include "Buffer.h"
#include <Containers/Array.h>
#include <Utility/Debug.h>
#include "Context.h"
@ -128,18 +127,6 @@ Int Buffer::size() {
return size;
}
#ifndef MAGNUM_TARGET_GLES
Containers::Array<char> Buffer::data() {
return subData(0, size());
}
Containers::Array<char> Buffer::subData(const GLintptr offset, const GLsizeiptr size) {
Containers::Array<char> 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<GLenum>(read->bindInternal(Target::CopyRead)), static_cast<GLenum>(write->bindInternal(Target::CopyWrite)), readOffset, writeOffset, size);

27
src/Buffer.h

@ -31,8 +31,9 @@
#include <cstddef>
#include <array>
#include <vector>
#include <Containers/Containers.h>
#include <Containers/Array.h>
#include <Containers/EnumSet.h>
#include <Utility/Assert.h>
#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<char> data();
template<class T> Containers::Array<T> 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<char> subData(GLintptr offset, GLsizeiptr size);
template<class T> Containers::Array<T> 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<class T> Containers::Array<T> 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<T>(0, bufferSize/sizeof(T));
}
template<class T> Containers::Array<T> inline Buffer::subData(const GLintptr offset, const GLsizeiptr size) {
Containers::Array<T> data(size);
if(size) (this->*getSubDataImplementation)(offset, size*sizeof(T), data);
return std::move(data);
}
#endif
}
#endif

2
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.

5
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

6
src/Context.cpp

@ -338,7 +338,11 @@ Context::Context() {
for(std::size_t i = future; i != versions.size(); ++i) {
const std::vector<Extension>& 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 */

34
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<class Image> 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<GLenum>(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<GLenum>(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<class Image> 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<GLenum>(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<GLenum>(coordinate), level, offset, image);
return this;
}
#endif
/**
* @brief Invalidate texture subimage
* @param level Mip level

43
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<class T> 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<class Image> 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<Image::Dimensions, GLsizei>()));
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<class Image> 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<GLsizei>(coordinate)), image, Vector2i(Math::Vector<Image::Dimensions, GLsizei>()));
/** @overload */
CubeMapTextureArray* setSubImage(Int level, const Vector3i& offset, BufferImage3D& image) {
DataHelper<3>::setSubImage(this, GL_TEXTURE_CUBE_MAP_ARRAY, level, offset, image);
return this;
}

2
src/Image.cpp

@ -26,7 +26,7 @@
namespace Magnum {
template<UnsignedInt dimensions> void Image<dimensions>::setData(const typename DimensionTraits<Dimensions, Int>::VectorType& size, ImageFormat format, ImageType type, void* data) {
template<UnsignedInt dimensions> void Image<dimensions>::setData(ImageFormat format, ImageType type, const typename DimensionTraits<Dimensions, Int>::VectorType& size, void* data) {
delete[] _data;
_format = format;
_type = type;

49
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<UnsignedInt dimensions> 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<Dimensions, Int>::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast<unsigned char*>(data)) {}
explicit Image(ImageFormat format, ImageType type, const typename DimensionTraits<Dimensions, Int>::VectorType& size, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast<unsigned char*>(data)) {}
/**
* @brief Constructor
@ -67,9 +65,28 @@ template<UnsignedInt dimensions> class Image: public AbstractImage {
*/
explicit Image(ImageFormat format, ImageType type): AbstractImage(format, type), _data(nullptr) {}
/** @brief Copying is not allowed */
Image(const Image<dimensions>&& other) = delete;
/** @brief Move constructor */
Image(Image<dimensions>&& other) noexcept;
/** @brief Copying is not allowed */
Image<dimensions>& operator=(const Image<dimensions>&& other) = delete;
/** @brief Move assignment */
Image<dimensions>& operator=(Image<dimensions>&& 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<dimensions>() const;
/** @brief %Image size */
typename DimensionTraits<Dimensions, Int>::VectorType size() const { return _size; }
@ -79,15 +96,15 @@ template<UnsignedInt dimensions> 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<Dimensions, Int>::VectorType& size, ImageFormat format, ImageType type, void* data);
void setData(ImageFormat format, ImageType type, const typename DimensionTraits<Dimensions, Int>::VectorType& size, void* data);
private:
Math::Vector<Dimensions, Int> _size;
@ -103,6 +120,22 @@ typedef Image<2> Image2D;
/** @brief Three-dimensional image */
typedef Image<3> Image3D;
template<UnsignedInt dimensions> inline Image<dimensions>::Image(Image<dimensions>&& other) noexcept: AbstractImage(std::move(other)), _size(std::move(other._size)), _data(std::move(other._data)) {
other._size = {};
other._data = nullptr;
}
template<UnsignedInt dimensions> inline Image<dimensions>& Image<dimensions>::operator=(Image<dimensions>&& other) noexcept {
AbstractImage::operator=(std::move(other));
std::swap(_size, other._size);
std::swap(_data, other._data);
return *this;
}
template<UnsignedInt dimensions> inline Image<dimensions>::operator ImageReference<dimensions>() const {
return ImageReference<dimensions>(this->format(), this->type(), _size, _data);
}
}
#endif

4
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. */

31
src/ImageWrapper.h → 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<UnsignedInt dimensions> class ImageWrapper: public AbstractImage {
template<UnsignedInt dimensions> 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<Dimensions, Int>::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast<unsigned char*>(data)) {}
constexpr explicit ImageReference(ImageFormat format, ImageType type, const typename DimensionTraits<Dimensions, Int>::VectorType& size, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast<unsigned char*>(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<Dimensions, Int>::VectorType& size, ImageFormat format, ImageType type): AbstractImage(format, type), _size(size), _data(nullptr) {}
constexpr explicit ImageReference(ImageFormat format, ImageType type, const typename DimensionTraits<Dimensions, Int>::VectorType& size): AbstractImage(format, type), _size(size), _data(nullptr) {}
/** @brief %Image size */
typename DimensionTraits<Dimensions, Int>::VectorType size() const { return _size; }
constexpr typename DimensionTraits<Dimensions, Int>::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<UnsignedInt dimensions> 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;
}

18
src/Magnum.h

@ -28,7 +28,7 @@
* @brief Forward declarations for Magnum namespace
*/
#include <corradeCompatibility.h>
#include <Utility/Utility.h>
#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<UnsignedInt> class ImageWrapper;
typedef ImageWrapper<1> ImageWrapper1D;
typedef ImageWrapper<2> ImageWrapper2D;
typedef ImageWrapper<3> ImageWrapper3D;
template<UnsignedInt> class ImageReference;
typedef ImageReference<1> ImageReference1D;
typedef ImageReference<2> ImageReference2D;
typedef ImageReference<3> ImageReference3D;
class Mesh;

30
src/Math/Geometry/Rectangle.h

@ -148,4 +148,34 @@ template<class T> Corrade::Utility::Debug operator<<(Corrade::Utility::Debug deb
}}}
namespace Corrade { namespace Utility {
/** @configurationvalue{Magnum::Math::Geometry::Rectangle} */
template<class T> struct ConfigurationValue<Magnum::Math::Geometry::Rectangle<T>> {
ConfigurationValue() = delete;
/** @brief Writes elements separated with spaces */
static std::string toString(const Magnum::Math::Geometry::Rectangle<T>& value, const ConfigurationValueFlags flags) {
return ConfigurationValue<Magnum::Math::Vector<4, T>>::toString(
reinterpret_cast<const Magnum::Math::Vector<4, T>&>(value), flags);
}
/** @brief Reads elements separated with whitespace */
static Magnum::Math::Geometry::Rectangle<T> fromString(const std::string& stringValue, const ConfigurationValueFlags flags) {
const auto vec = ConfigurationValue<Magnum::Math::Vector<4, T>>::fromString(stringValue, flags);
return {{vec[0], vec[1]}, {vec[2], vec[3]}};
}
};
#ifndef DOXYGEN_GENERATING_OUTPUT
extern template struct MAGNUM_EXPORT ConfigurationValue<Magnum::Math::Geometry::Rectangle<Magnum::Float>>;
extern template struct MAGNUM_EXPORT ConfigurationValue<Magnum::Math::Geometry::Rectangle<Magnum::Int>>;
extern template struct MAGNUM_EXPORT ConfigurationValue<Magnum::Math::Geometry::Rectangle<Magnum::UnsignedInt>>;
#ifndef MAGNUM_TARGET_GLES
extern template struct MAGNUM_EXPORT ConfigurationValue<Magnum::Math::Geometry::Rectangle<Magnum::Double>>;
#endif
#endif
}}
#endif

16
src/Math/Geometry/Test/RectangleTest.cpp

@ -24,6 +24,7 @@
#include <sstream>
#include <TestSuite/Tester.h>
#include <Utility/Configuration.h>
#include "Math/Geometry/Rectangle.h"
@ -44,6 +45,7 @@ class RectangleTest: public Corrade::TestSuite::Tester {
void size();
void debug();
void configuration();
};
typedef Geometry::Rectangle<Float> 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>("rectangle"), rect);
}
}}}}
CORRADE_TEST_MAIN(Magnum::Math::Geometry::Test::RectangleTest)

38
src/Math/Geometry/instantiation.cpp

@ -0,0 +1,38 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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<Magnum::Math::Geometry::Rectangle<Magnum::Float>>;
template struct ConfigurationValue<Magnum::Math::Geometry::Rectangle<Magnum::Int>>;
template struct ConfigurationValue<Magnum::Math::Geometry::Rectangle<Magnum::UnsignedInt>>;
#ifndef MAGNUM_TARGET_GLES
template struct ConfigurationValue<Magnum::Math::Geometry::Rectangle<Magnum::Double>>;
#endif
#endif
}}

6
src/MeshTools/RemoveDuplicates.h

@ -129,7 +129,11 @@ template<class Vertex, std::size_t vertexSize> void RemoveDuplicates<Vertex, ver
exists, change vertex pointer of the face to already
existing vertex */
HashedVertex v(*it, table.size());
auto result = table.insert(std::pair<Math::Vector<vertexSize, std::size_t>, HashedVertex>(Math::Vector<vertexSize, std::size_t>::from(index), v));
#ifndef CORRADE_GCC46_COMPATIBILITY
auto result = table.emplace(Math::Vector<vertexSize, std::size_t>::from(index), v);
#else
auto result = table.insert({Math::Vector<vertexSize, std::size_t>::from(index), v});
#endif
*it = result.first->second.newIndex;
}

6
src/ResourceManager.h

@ -471,7 +471,11 @@ template<class T> void ResourceManagerData<T>::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;

56
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š <mosra@centrum.cz>
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 <TestSuite/Tester.h>
#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

265
src/Test/BufferGLTest.cpp

@ -0,0 +1,265 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Containers/Array.h>
#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<Int> contents = buffer.data<Int>();
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<Int> subContents = buffer.subData<Int>(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<Extensions::GL::OES::mapbuffer>())
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<char*>(buffer.map(Buffer::MapAccess::ReadWrite));
#else
char* contents = reinterpret_cast<char*>(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<char> changedContents = buffer.data<char>();
CORRADE_COMPARE(changedContents.size(), 5);
CORRADE_COMPARE(changedContents[3], 107);
#endif
}
#endif
void BufferGLTest::mapRange() {
#ifndef MAGNUM_TARGET_GLES2
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
#else
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
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<char*>(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<char> changedContents = buffer.data<char>();
CORRADE_COMPARE(changedContents.size(), 5);
CORRADE_COMPARE(changedContents[4], 107);
#endif
}
void BufferGLTest::mapRangeExplicitFlush() {
#ifndef MAGNUM_TARGET_GLES2
if(!Context::current()->isExtensionSupported<Extensions::GL::ARB::map_buffer_range>())
CORRADE_SKIP(Extensions::GL::ARB::map_buffer_range::string() + std::string(" is not supported"));
#else
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::map_buffer_range>())
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<char*>(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<char*>(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<char> changedContents = buffer.data<char>();
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<char> subContents = buffer2.subData<char>(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<Extensions::GL::ARB::invalidate_subdata>())
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)

10
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()

85
src/Test/ImageTest.cpp

@ -0,0 +1,85 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <TestSuite/Tester.h>
#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)

119
src/Text/AbstractFont.cpp

@ -22,7 +22,11 @@
DEALINGS IN THE SOFTWARE.
*/
#include "Text/AbstractFont.h"
#include "AbstractFont.h"
#include <fstream>
#include <Containers/Array.h>
#include <Utility/Unicode.h>
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<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& 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<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& 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<const unsigned char> 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<const unsigned char>, 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<unsigned char> data(in.tellg());
/* Read data, close */
in.seekg(0, std::ios::beg);
in.read(reinterpret_cast<char*>(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() {}

187
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<Feature, UnsignedByte> 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<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& 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<const unsigned char> 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<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& data, Float size);
/** @brief Implementation for openSingleData() */
virtual void doOpenSingleData(Containers::ArrayReference<const unsigned char> 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<Rectangle, Rectangle, Vector2> renderGlyph(const Vector2& cursorPosition, UnsignedInt i) = 0;
virtual std::tuple<Rectangle, Rectangle, Vector2> renderGlyph(UnsignedInt i) = 0;
#ifdef DOXYGEN_GENERATING_OUTPUT
private:

228
src/Text/AbstractFontConverter.cpp

@ -0,0 +1,228 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <algorithm>
#include <fstream>
#include <Containers/Array.h>
#include <Utility/Assert.h>
#include <Utility/Unicode.h>
namespace Magnum { namespace Text {
AbstractFontConverter::AbstractFontConverter() = default;
AbstractFontConverter::AbstractFontConverter(PluginManager::AbstractManager* manager, std::string plugin): PluginManager::AbstractPlugin(manager, std::move(plugin)) {}
std::vector<std::pair<std::string, Containers::Array<unsigned char>>> 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<std::pair<std::string, Containers::Array<unsigned char>>> 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<std::pair<std::string, Containers::Array<unsigned char>>> out;
out.emplace_back(filename, std::move(doExportFontToSingleData(font, cache, characters)));
return std::move(out);
}
Containers::Array<unsigned char> 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<unsigned char> 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<const char*>(d.second.begin()), d.second.size());
}
return true;
}
std::vector<std::pair<std::string, Containers::Array<unsigned char>>> 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<std::pair<std::string, Containers::Array<unsigned char>>> AbstractFontConverter::doExportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const {
CORRADE_ASSERT(!(features() & Feature::MultiFile),
"Text::AbstractFontConverter::exportGlyphCacheToData(): feature advertised but not implemented", {});
std::vector<std::pair<std::string, Containers::Array<unsigned char>>> out;
out.emplace_back(filename, std::move(doExportGlyphCacheToSingleData(cache)));
return std::move(out);
}
Containers::Array<unsigned char> 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<unsigned char> 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<const char*>(d.second.begin()), d.second.size());
}
return true;
}
GlyphCache* AbstractFontConverter::importGlyphCacheFromData(const std::vector<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& 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<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& 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<const unsigned char> 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 unsigned char>) 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<unsigned char> data(in.tellg());
/* Read data, close */
in.seekg(0, std::ios::beg);
in.read(reinterpret_cast<char*>(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);
}
}}

317
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š <mosra@centrum.cz>
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 <PluginManager/AbstractPlugin.h>
#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<Feature, UnsignedByte> 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<std::pair<std::string, Containers::Array<unsigned char>>> 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<unsigned char> 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<std::pair<std::string, Containers::Array<unsigned char>>> 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<unsigned char> 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<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& 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<const unsigned char> 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<std::pair<std::string, Containers::Array<unsigned char>>> doExportFontToData(AbstractFont* font, GlyphCache* cache, const std::string& filename, const std::u32string& characters) const;
/** @brief Implementation for exportFontToSingleData() */
virtual Containers::Array<unsigned char> 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<std::pair<std::string, Containers::Array<unsigned char>>> doExportGlyphCacheToData(GlyphCache* cache, const std::string& filename) const;
/** @brief Implementation for exportGlyphCacheToSingleData() */
virtual Containers::Array<unsigned char> 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<std::pair<std::string, Containers::ArrayReference<const unsigned char>>>& data) const;
/** @brief Implementation for importGlyphCacheFromSingleData() */
virtual GlyphCache* doImportGlyphCacheFromSingleData(Containers::ArrayReference<const unsigned char> 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

6
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()

43
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<Extensions::GL::EXT::texture_rg>() ?
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<Extensions::GL::EXT::texture_rg>() ?
TextureFormat::Red : TextureFormat::RGB;
if(internalFormat == TextureFormat::RGB)
#ifdef MAGNUM_TARGET_GLES2
if(!Context::current()->isExtensionSupported<Extensions::GL::EXT::texture_rg>())
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<Extensions::GL::EXT::texture_rg>()) {
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);
}
}}

10
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;

32
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<Rectanglei> GlyphCache::reserve(const std::vector<Vector2i>& 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<Vector2i, Rectanglei>()),
"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);
}

69
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<Vector2i, Rectanglei> 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<UnsignedInt, std::pair<Vector2i, Rectanglei>>::const_iterator begin() const {
return glyphs.begin();
}
/** @brief Iterator access to cache data */
std::unordered_map<UnsignedInt, std::pair<Vector2i, Rectanglei>>::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<Rectanglei> reserve(const std::vector<Vector2i>& 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<UnsignedInt, std::pair<Vector2i, Rectanglei>> glyphs;
};

229
src/Text/Test/AbstractFontConverterTest.cpp

@ -0,0 +1,229 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Containers/Array.h>
#include <TestSuite/Tester.h>
#include <TestSuite/Compare/FileToString.h>
#include <Utility/Directory.h>
#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<unsigned char> 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<unsigned char> doExportFontToSingleData(AbstractFont*, GlyphCache*, const std::u32string&) const override {
Containers::Array<unsigned char> 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<std::pair<std::string, Containers::Array<unsigned char>>> doExportFontToData(AbstractFont*, GlyphCache*, const std::string& filename, const std::u32string&) const override {
Containers::Array<unsigned char> file(1);
file[0] = 0xf0;
Containers::Array<unsigned char> data(2);
data[0] = 0xfe;
data[1] = 0xed;
std::vector<std::pair<std::string, Containers::Array<unsigned char>>> 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<unsigned char> doExportGlyphCacheToSingleData(GlyphCache*) const override {
Containers::Array<unsigned char> 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<std::pair<std::string, Containers::Array<unsigned char>>> doExportGlyphCacheToData(GlyphCache*, const std::string& filename) const override {
Containers::Array<unsigned char> file(1);
file[0] = 0xf0;
Containers::Array<unsigned char> data(2);
data[0] = 0xfe;
data[1] = 0xed;
std::vector<std::pair<std::string, Containers::Array<unsigned char>>> 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<const unsigned char> data) const override {
if(data.size() == 1 && data[0] == 0xa5) return reinterpret_cast<GlyphCache*>(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<GlyphCache*>(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<GlyphCache*>(0xdeadbeef));
}
}}}
CORRADE_TEST_MAIN(Magnum::Text::Test::AbstractFontConverterTest)

94
src/Text/Test/AbstractFontTest.cpp

@ -0,0 +1,94 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Containers/Array.h>
#include <TestSuite/Tester.h>
#include <Utility/Directory.h>
#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<const unsigned char> 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)

36
src/Text/Test/CMakeLists.txt

@ -0,0 +1,36 @@
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
#
# 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()

92
src/Text/Test/GlyphCacheGLTest.cpp

@ -0,0 +1,92 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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)

247
src/Text/Test/TextRendererGLTest.cpp

@ -0,0 +1,247 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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<Rectangle, Rectangle, Vector2> 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<Vector2> positions;
std::vector<Vector2> textureCoordinates;
std::vector<UnsignedInt> 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<Vector2>{
{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<Vector2>{
{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<UnsignedInt>{
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<Float> vertices = vertexBuffer.data<Float>();
CORRADE_COMPARE(std::vector<Float>(vertices.begin(), vertices.end()), (std::vector<Float>{
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<UnsignedByte> indices = indexBuffer.data<UnsignedByte>();
CORRADE_COMPARE(std::vector<UnsignedByte>(indices.begin(), indices.end()), (std::vector<UnsignedByte>{
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<UnsignedByte> indices = renderer.indexBuffer()->data<UnsignedByte>();
CORRADE_COMPARE(std::vector<UnsignedByte>(indices.begin(), indices.end()), (std::vector<UnsignedByte>{
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<Float> vertices = renderer.vertexBuffer()->subData<Float>(0, 48);
CORRADE_COMPARE(std::vector<Float>(vertices.begin(), vertices.end()), (std::vector<Float>{
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)

1
src/Text/Test/data.bin

@ -0,0 +1 @@
<EFBFBD>

26
src/Text/Test/testConfigure.h.cmake

@ -0,0 +1,26 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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}"

1
src/Text/Text.h

@ -35,6 +35,7 @@
namespace Magnum { namespace Text {
class AbstractFont;
class AbstractFontConverter;
class AbstractLayouter;
class DistanceFieldGlyphCache;
class GlyphCache;

87
src/Text/TextRenderer.cpp

@ -37,11 +37,11 @@ namespace {
template<class T> void createIndices(void* output, const UnsignedInt glyphCount) {
T* const out = reinterpret_cast<T*>(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<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>,
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<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>,
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<Vector2>, std::vector<Vector2>, std::vector<UnsignedInt>,
std::vector<UnsignedInt> indices(layouter->glyphCount()*6);
createIndices<UnsignedInt>(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<Mesh, Rectangle> AbstractTextRenderer::render(AbstractFont* const fon
std::vector<Vertex> 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<Mesh, Rectangle> 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<Mesh, Rectangle> 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<UnsignedInt dimensions> std::tuple<Mesh, Rectangle> TextRenderer<dimens
return std::move(r);
}
AbstractTextRenderer::AbstractTextRenderer(AbstractFont* const font, const GlyphCache* const cache, Float size): vertexBuffer(Buffer::Target::Array), indexBuffer(Buffer::Target::ElementArray), font(font), cache(cache), size(size), _capacity(0) {
AbstractTextRenderer::AbstractTextRenderer(AbstractFont* const font, const GlyphCache* const cache, Float size): _vertexBuffer(Buffer::Target::Array), _indexBuffer(Buffer::Target::ElementArray), font(font), cache(cache), size(size), _capacity(0) {
#ifndef MAGNUM_TARGET_GLES
MAGNUM_ASSERT_EXTENSION_SUPPORTED(Extensions::GL::ARB::map_buffer_range);
#else
@ -202,7 +222,7 @@ AbstractTextRenderer::~AbstractTextRenderer() {}
template<UnsignedInt dimensions> TextRenderer<dimensions>::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<dimensions>::Position(Shaders::AbstractVector<dimensions>::Position::Components::Two),
typename Shaders::AbstractVector<dimensions>::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<UnsignedByte>(indices, glyphCount);
else if(vertexCount < 65535)
createIndices<UnsignedShort>(indices, glyphCount);
else
createIndices<UnsignedInt>(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<Vertex*>(vertexBuffer.map(0, layouter->glyphCount()*4*sizeof(Vertex),
Vertex* const vertices = static_cast<Vertex*>(_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);

10
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<Mesh, Rectangle> 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;

43
src/Texture.h

@ -324,7 +324,7 @@ template<UnsignedInt dimensions> 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<dimensions>* image) {
void image(Int level, Image<dimensions>& image) {
AbstractTexture::image<dimensions>(_target, level, image);
}
@ -334,10 +334,10 @@ template<UnsignedInt dimensions> 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<dimensions>* image, Buffer::Usage usage) {
void image(Int level, BufferImage<dimensions>& image, Buffer::Usage usage) {
AbstractTexture::image<dimensions>(_target, level, image, usage);
}
#endif
@ -346,12 +346,9 @@ template<UnsignedInt dimensions> 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<UnsignedInt dimensions> class Texture: public AbstractTexture {
* @fn_gl_extension{TextureImage2D,EXT,direct_state_access}/
* @fn_gl_extension{TextureImage3D,EXT,direct_state_access}
*/
template<class Image> Texture<Dimensions>* setImage(Int level, TextureFormat internalFormat, Image* image) {
Texture<Dimensions>* setImage(Int level, TextureFormat internalFormat, const ImageReference<dimensions>& image) {
DataHelper<Dimensions>::setImage(this, _target, level, internalFormat, image);
return this;
}
#ifndef MAGNUM_TARGET_GLES2
/** @overload */
Texture<Dimensions>* setImage(Int level, TextureFormat internalFormat, BufferImage<dimensions>& image) {
DataHelper<Dimensions>::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<UnsignedInt dimensions> class Texture: public AbstractTexture {
* @fn_gl_extension{TextureSubImage2D,EXT,direct_state_access}/
* @fn_gl_extension{TextureSubImage3D,EXT,direct_state_access}
*/
template<class Image> Texture<Dimensions>* setSubImage(Int level, const typename DimensionTraits<Dimensions, Int>::VectorType& offset, Image* image) {
Texture<Dimensions>* setSubImage(Int level, const typename DimensionTraits<Dimensions, Int>::VectorType& offset, const ImageReference<dimensions>& image) {
DataHelper<Dimensions>::setSubImage(this, _target, level, offset, image);
return this;
}
#ifndef MAGNUM_TARGET_GLES2
/** @overload */
Texture<Dimensions>* setSubImage(Int level, const typename DimensionTraits<Dimensions, Int>::VectorType& offset, BufferImage<dimensions>& image) {
DataHelper<Dimensions>::setSubImage(this, _target, level, offset, image);
return this;
}
#endif
/**
* @brief Invalidate texture subimage

49
src/Trade/AbstractImageConverter.cpp

@ -24,6 +24,8 @@
#include "AbstractImageConverter.h"
#include <fstream>
#include <Containers/Array.h>
#include <Utility/Assert.h>
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<const unsigned char*, std::size_t> 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<unsigned char> 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<unsigned char> 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<const char*>(data.begin()), data.size());
return true;
}
}}

72
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<const unsigned char*, std::size_t> convertToData(const Image2D* image) const;
Containers::Array<unsigned char> 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<unsigned char> 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)

461
src/Trade/AbstractImporter.cpp

@ -24,6 +24,8 @@
#include "AbstractImporter.h"
#include <fstream>
#include <Containers/Array.h>
#include <Utility/Assert.h>
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<const unsigned char> 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<const unsigned char>) {
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<unsigned char> data(in.tellg());
/* Read data, close */
in.seekg(0, std::ios::beg);
in.read(reinterpret_cast<char*>(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; }
}}

327
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<std::size_t size, class T> bool openData(const T(&data)[size]) {
return openData(data, size*sizeof(T));
}
bool openData(Containers::ArrayReference<const unsigned char> 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<const unsigned char> 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)

45
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<UnsignedInt dimensions> class ImageData: public AbstractImage {
@ -47,19 +45,38 @@ template<UnsignedInt dimensions> 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<Dimensions, Int>::VectorType& size, ImageFormat format, ImageType type, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast<unsigned char*>(data)) {}
explicit ImageData(ImageFormat format, ImageType type, const typename DimensionTraits<Dimensions, Int>::VectorType& size, void* data): AbstractImage(format, type), _size(size), _data(reinterpret_cast<unsigned char*>(data)) {}
/** @brief Copying is not allowed */
ImageData(const ImageData<dimensions>&& other) = delete;
/** @brief Move constructor */
ImageData(ImageData<dimensions>&& other) noexcept;
/** @brief Copying is not allowed */
ImageData<dimensions>& operator=(const ImageData<dimensions>&& other) = delete;
/** @brief Move assignment */
ImageData<dimensions>& operator=(ImageData<dimensions>&& 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<dimensions>() const;
/** @brief %Image size */
typename DimensionTraits<Dimensions, Int>::VectorType size() const { return _size; }
@ -81,6 +98,22 @@ typedef ImageData<2> ImageData2D;
/** @brief Three-dimensional image */
typedef ImageData<3> ImageData3D;
template<UnsignedInt dimensions> inline ImageData<dimensions>::ImageData(ImageData<dimensions>&& other) noexcept: AbstractImage(std::move(other)), _size(std::move(other._size)), _data(std::move(other._data)) {
other._size = {};
other._data = nullptr;
}
template<UnsignedInt dimensions> inline ImageData<dimensions>& ImageData<dimensions>::operator=(ImageData<dimensions>&& other) noexcept {
AbstractImage::operator=(std::move(other));
std::swap(_size, other._size);
std::swap(_data, other._data);
return *this;
}
template<UnsignedInt dimensions> inline ImageData<dimensions>::operator ImageReference<dimensions>() const {
return ImageReference<dimensions>(this->format(), this->type(), _size, _data);
}
}}
#endif

75
src/Trade/Test/AbstractImageConverterTest.cpp

@ -0,0 +1,75 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Containers/Array.h>
#include <TestSuite/Tester.h>
#include <TestSuite/Compare/FileToString.h>
#include <Utility/Directory.h>
#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<unsigned char> doExportToData(const Image2D* image) const override {
Containers::Array<unsigned char> 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)

69
src/Trade/Test/AbstractImporterTest.cpp

@ -0,0 +1,69 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <Containers/Array.h>
#include <TestSuite/Tester.h>
#include <Utility/Directory.h>
#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<const unsigned char> 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)

8
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)

85
src/Trade/Test/ImageDataTest.cpp

@ -0,0 +1,85 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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 <TestSuite/Tester.h>
#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)

1
src/Trade/Test/file.bin

@ -0,0 +1 @@
<EFBFBD>

26
src/Trade/Test/testConfigure.h.cmake

@ -0,0 +1,26 @@
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013 Vladimír Vondruš <mosra@centrum.cz>
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}"
Loading…
Cancel
Save