From 7f705da5644092d48b48aaacbe3dc232ff5149fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20K=C3=A4fer?= Date: Thu, 11 Jun 2015 11:07:28 -0400 Subject: Sprite store API --- include/mbgl/util/exception.hpp | 5 +++ src/mbgl/annotation/sprite_image.cpp | 27 +++++++++++++++ src/mbgl/annotation/sprite_image.hpp | 36 ++++++++++++++++++++ src/mbgl/annotation/sprite_store.cpp | 40 ++++++++++++++++++++++ src/mbgl/annotation/sprite_store.hpp | 52 +++++++++++++++++++++++++++++ src/mbgl/geometry/sprite_atlas.hpp | 2 ++ test/annotations/sprite_image.cpp | 64 ++++++++++++++++++++++++++++++++++++ test/annotations/sprite_store.cpp | 51 ++++++++++++++++++++++++++++ test/test.gypi | 3 ++ 9 files changed, 280 insertions(+) create mode 100644 src/mbgl/annotation/sprite_image.cpp create mode 100644 src/mbgl/annotation/sprite_image.hpp create mode 100644 src/mbgl/annotation/sprite_store.cpp create mode 100644 src/mbgl/annotation/sprite_store.hpp create mode 100644 test/annotations/sprite_image.cpp create mode 100644 test/annotations/sprite_store.cpp diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp index da61aa482a..31fa693f8d 100644 --- a/include/mbgl/util/exception.hpp +++ b/include/mbgl/util/exception.hpp @@ -11,6 +11,11 @@ struct Exception : std::runtime_error { inline Exception(const std::string &msg) : std::runtime_error(msg) {} }; +struct SpriteImageException : Exception { + inline SpriteImageException(const char *msg) : Exception(msg) {} + inline SpriteImageException(const std::string &msg) : Exception(msg) {} +}; + struct GlyphRangeLoadingException : Exception { inline GlyphRangeLoadingException(const char *msg) : Exception(msg) {} inline GlyphRangeLoadingException(const std::string &msg) : Exception(msg) {} diff --git a/src/mbgl/annotation/sprite_image.cpp b/src/mbgl/annotation/sprite_image.cpp new file mode 100644 index 0000000000..9cfe9c9fc8 --- /dev/null +++ b/src/mbgl/annotation/sprite_image.cpp @@ -0,0 +1,27 @@ +#include + +#include + +#include + +namespace mbgl { + +SpriteImage::SpriteImage(const uint16_t width_, + const uint16_t height_, + const float ratio_, + std::string&& data_) + : width(width_), + height(height_), + ratio(ratio_), + pixelWidth(std::ceil(width * ratio)), + pixelHeight(std::ceil(height * ratio)), + data(std::move(data_)) { + const size_t size = pixelWidth * pixelHeight * 4; + if (size == 0) { + throw util::SpriteImageException("Sprite image dimensions may not be zero"); + } else if (size != data.size()) { + throw util::SpriteImageException("Sprite image pixel count mismatch"); + } +} + +} // namespace mbgl diff --git a/src/mbgl/annotation/sprite_image.hpp b/src/mbgl/annotation/sprite_image.hpp new file mode 100644 index 0000000000..2d0ac920a8 --- /dev/null +++ b/src/mbgl/annotation/sprite_image.hpp @@ -0,0 +1,36 @@ +#ifndef MBGL_ANNOTATIONS_SPRITE_IMAGE +#define MBGL_ANNOTATIONS_SPRITE_IMAGE + +#include +#include + +#include +#include +#include + +namespace mbgl { + +class SpriteImage : private util::noncopyable { +public: + SpriteImage(uint16_t width, uint16_t height, float ratio, std::string&& data); + + // Logical dimensions of the sprite image. + const uint16_t width; + const uint16_t height; + + // Pixel ratio of the sprite image. + const float ratio; + + // Physical dimensions of the sprite image. + const uint16_t pixelWidth; + const uint16_t pixelHeight; + + // A string of an RGBA8 representation of the sprite. It must have exactly + // (width * ratio) * (height * ratio) * 4 (RGBA) bytes. The scan lines may + // not have gaps between them (i.e. stride == 0). + const std::string data; +}; + +} + +#endif diff --git a/src/mbgl/annotation/sprite_store.cpp b/src/mbgl/annotation/sprite_store.cpp new file mode 100644 index 0000000000..733d2c7b49 --- /dev/null +++ b/src/mbgl/annotation/sprite_store.cpp @@ -0,0 +1,40 @@ +#include + +#include + +namespace mbgl { + +SpriteStore::SpriteStore(const float ratio_) : ratio(ratio_) { +} + +void SpriteStore::setSprite(const std::string& name, std::shared_ptr sprite) { + if (sprite && sprite->ratio != ratio) { + throw util::SpriteImageException("Sprite image has wrong pixel ratio"); + } + std::lock_guard lock(mutex); + if (sprite) { + sprites.emplace(name, sprite); + } else { + sprites.erase(name); + } + dirty.emplace(name, sprite); +} + +void SpriteStore::removeSprite(const std::string& name) { + setSprite(name); +} + +std::shared_ptr SpriteStore::getSprite(const std::string& name) { + std::lock_guard lock(mutex); + const auto it = sprites.find(name); + return it != sprites.end() ? it->second : nullptr; +} + +SpriteStore::Sprites SpriteStore::getDirty() { + Sprites result; + std::lock_guard lock(mutex); + dirty.swap(result); + return result; +} + +} // namespace mbgl diff --git a/src/mbgl/annotation/sprite_store.hpp b/src/mbgl/annotation/sprite_store.hpp new file mode 100644 index 0000000000..3e81a4daf0 --- /dev/null +++ b/src/mbgl/annotation/sprite_store.hpp @@ -0,0 +1,52 @@ +#ifndef MBGL_ANNOTATION_SPRITE_STORE +#define MBGL_ANNOTATION_SPRITE_STORE + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +// The SpriteStore object holds Sprite images. +class SpriteStore : private util::noncopyable { + using Sprites = std::map>; + +public: + SpriteStore(const float ratio); + + // Adds/replaces a Sprite image. + void setSprite(const std::string&, std::shared_ptr = nullptr); + + // Removes a Sprite. + void removeSprite(const std::string&); + + // Obtains a Sprite image. + std::shared_ptr getSprite(const std::string&); + + // Returns Sprite images that changed since the last invocation of this function. + Sprites getDirty(); + +private: + const float ratio; + + // Lock for sprites and dirty maps. + std::mutex mutex; + + // Stores all current sprites. + Sprites sprites; + + // Stores all Sprite IDs that changed since the last invocation. + Sprites dirty; +}; + +} // namespace mbgl + +#endif diff --git a/src/mbgl/geometry/sprite_atlas.hpp b/src/mbgl/geometry/sprite_atlas.hpp index d34013cd6c..3da472fa72 100644 --- a/src/mbgl/geometry/sprite_atlas.hpp +++ b/src/mbgl/geometry/sprite_atlas.hpp @@ -46,8 +46,10 @@ public: // This getter attempts to read the image from the sprite if it is already loaded. // In that case, it copies it into the sprite atlas and returns the dimensions. // Otherwise, it returns a 0/0/0/0 rect. + // This function is used during bucket creation. Rect getImage(const std::string& name, const bool wrap); + // This function is used for getting the position during render time. SpriteAtlasPosition getPosition(const std::string& name, bool repeating = false); // Binds the atlas texture to the GPU, and uploads data if it is out of date. diff --git a/test/annotations/sprite_image.cpp b/test/annotations/sprite_image.cpp new file mode 100644 index 0000000000..30542778fd --- /dev/null +++ b/test/annotations/sprite_image.cpp @@ -0,0 +1,64 @@ +#include "../fixtures/util.hpp" + +#include +#include + +using namespace mbgl; + +TEST(Annotations, SpriteImageZeroWidth) { + try { + SpriteImage(0, 16, 2, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); + } +} + +TEST(Annotations, SpriteImageZeroHeight) { + try { + SpriteImage(16, 0, 2, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); + } +} + +TEST(Annotations, SpriteImageZeroRatio) { + try { + SpriteImage(16, 16, 0, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); + } +} + +TEST(Annotations, SpriteImageMismatchedData) { + try { + SpriteImage(16, 16, 2, ""); + FAIL() << "Expected exception"; + } catch (util::SpriteImageException& ex) { + EXPECT_STREQ("Sprite image pixel count mismatch", ex.what()); + } +} + +TEST(Annotations, SpriteImage) { + std::string pixels(32 * 24 * 4, '\0'); + SpriteImage sprite(16, 12, 2, std::move(pixels)); + EXPECT_EQ(16, sprite.width); + EXPECT_EQ(32, sprite.pixelWidth); + EXPECT_EQ(12, sprite.height); + EXPECT_EQ(24, sprite.pixelHeight); + EXPECT_EQ(2, sprite.ratio); + EXPECT_EQ(32u * 24 * 4, sprite.data.size()); +} + +TEST(Annotations, SpriteImageFractionalRatio) { + std::string pixels(20 * 12 * 4, '\0'); + SpriteImage sprite(13, 8, 1.5, std::move(pixels)); + EXPECT_EQ(13, sprite.width); + EXPECT_EQ(20, sprite.pixelWidth); + EXPECT_EQ(8, sprite.height); + EXPECT_EQ(12, sprite.pixelHeight); + EXPECT_EQ(1.5, sprite.ratio); + EXPECT_EQ(20u * 12 * 4, sprite.data.size()); +} diff --git a/test/annotations/sprite_store.cpp b/test/annotations/sprite_store.cpp new file mode 100644 index 0000000000..287efaedab --- /dev/null +++ b/test/annotations/sprite_store.cpp @@ -0,0 +1,51 @@ +#include "../fixtures/util.hpp" + +#include + +using namespace mbgl; + +TEST(Annotations, SpriteStore) { + const auto sprite1 = std::make_shared(8, 8, 2, std::string(16 * 16 * 4, '\0')); + const auto sprite2 = std::make_shared(8, 8, 2, std::string(16 * 16 * 4, '\0')); + const auto sprite3 = std::make_shared(8, 8, 2, std::string(16 * 16 * 4, '\0')); + + using Sprites = std::map>; + SpriteStore store(2); + + // Adding single + store.setSprite("one", sprite1); + EXPECT_EQ(Sprites({ + { "one", sprite1 }, + }), store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); + + // Adding multiple + store.setSprite("two", sprite2); + store.setSprite("three", sprite3); + EXPECT_EQ(Sprites({ + { "two", sprite2 }, + { "three", sprite3 }, + }), store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); + + // Removing + store.removeSprite("one"); + store.removeSprite("two"); + EXPECT_EQ(Sprites({ + { "one", nullptr }, + { "two", nullptr }, + }), store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); + + // Accessing + EXPECT_EQ(sprite3, store.getSprite("three")); + EXPECT_EQ(nullptr, store.getSprite("two")); + EXPECT_EQ(nullptr, store.getSprite("four")); + + // Overwriting + store.setSprite("three", sprite1); + EXPECT_EQ(Sprites({ + { "three", sprite1 }, + }), store.getDirty()); + EXPECT_EQ(Sprites(), store.getDirty()); +} diff --git a/test/test.gypi b/test/test.gypi index 6afee47a44..588c61f59a 100644 --- a/test/test.gypi +++ b/test/test.gypi @@ -37,6 +37,9 @@ 'miscellaneous/assert.cpp', + 'annotations/sprite_image.cpp', + 'annotations/sprite_store.cpp', + 'api/api_misuse.cpp', 'api/repeated_render.cpp', 'api/set_style.cpp', -- cgit v1.2.1