summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2019-12-09 15:09:42 +0100
committerAlexander Shalamov <alexander.shalamov@mapbox.com>2020-01-15 15:02:11 +0200
commit62b0f4cde289e5918c41d5b69b0a03baa6821862 (patch)
tree2042319da9abd756564dcbba7812d4868a177800
parent4b171cccf1c4012f8962b022f86c4ac8d73f09df (diff)
downloadqtlocation-mapboxgl-62b0f4cde289e5918c41d5b69b0a03baa6821862.tar.gz
[core] Add stretches and content to style::Image
-rw-r--r--include/mbgl/style/image.hpp46
-rw-r--r--include/mbgl/util/exception.hpp6
-rw-r--r--platform/android/src/map/image.cpp2
-rw-r--r--platform/android/src/native_map_view.cpp2
-rw-r--r--src/mbgl/sprite/sprite_parser.cpp2
-rw-r--r--src/mbgl/style/image.cpp23
-rw-r--r--src/mbgl/style/image_impl.cpp57
-rw-r--r--src/mbgl/style/image_impl.hpp15
-rw-r--r--test/sprite/sprite_parser.test.cpp34
-rw-r--r--test/style/style_image.test.cpp85
10 files changed, 229 insertions, 43 deletions
diff --git a/include/mbgl/style/image.hpp b/include/mbgl/style/image.hpp
index ff3bfedf46..bbea081c71 100644
--- a/include/mbgl/style/image.hpp
+++ b/include/mbgl/style/image.hpp
@@ -2,15 +2,52 @@
#include <mbgl/util/image.hpp>
#include <mbgl/util/immutable.hpp>
+#include <mbgl/util/optional.hpp>
#include <string>
+#include <utility>
+#include <vector>
namespace mbgl {
namespace style {
+using ImageStretch = std::pair<float, float>;
+using ImageStretches = std::vector<ImageStretch>;
+
+class ImageContent {
+public:
+ float left;
+ float top;
+ float right;
+ float bottom;
+
+ bool operator==(const ImageContent& rhs) const {
+ return left == rhs.left && top == rhs.top && right == rhs.right && bottom == rhs.bottom;
+ }
+};
+
class Image {
public:
- Image(std::string id, PremultipliedImage&&, float pixelRatio, bool sdf = false);
+ Image(std::string id,
+ PremultipliedImage&&,
+ float pixelRatio,
+ bool sdf,
+ ImageStretches stretchX = {},
+ ImageStretches stretchY = {},
+ optional<ImageContent> content = nullopt);
+ Image(std::string id,
+ PremultipliedImage&& image,
+ float pixelRatio,
+ ImageStretches stretchX = {},
+ ImageStretches stretchY = {},
+ optional<ImageContent> content = nullopt)
+ : Image(std::move(id),
+ std::move(image),
+ pixelRatio,
+ false,
+ std::move(stretchX),
+ std::move(stretchY),
+ std::move(content)) {}
Image(const Image&);
std::string getID() const;
@@ -23,6 +60,13 @@ public:
// Whether this image should be interpreted as a signed distance field icon.
bool isSdf() const;
+ // Stretch areas of this image.
+ const ImageStretches& getStretchX() const;
+ const ImageStretches& getStretchY() const;
+
+ // The space where text can be fit into this image.
+ const ImageContent& getContent() const;
+
class Impl;
Immutable<Impl> baseImpl;
};
diff --git a/include/mbgl/util/exception.hpp b/include/mbgl/util/exception.hpp
index a9804e96c5..f0d7c64f42 100644
--- a/include/mbgl/util/exception.hpp
+++ b/include/mbgl/util/exception.hpp
@@ -10,9 +10,9 @@ struct Exception : std::runtime_error {
Exception(const std::string &msg) : std::runtime_error(msg) {}
};
-struct SpriteImageException : Exception {
- SpriteImageException(const char *msg) : Exception(msg) {}
- SpriteImageException(const std::string &msg) : Exception(msg) {}
+struct StyleImageException : Exception {
+ StyleImageException(const char *msg) : Exception(msg) {}
+ StyleImageException(const std::string &msg) : Exception(msg) {}
};
struct MisuseException : Exception {
diff --git a/platform/android/src/map/image.cpp b/platform/android/src/map/image.cpp
index a91cc938ed..10c32d2577 100644
--- a/platform/android/src/map/image.cpp
+++ b/platform/android/src/map/image.cpp
@@ -26,7 +26,7 @@ mbgl::style::Image Image::getImage(jni::JNIEnv& env, const jni::Object<Image>& i
mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(width), static_cast<uint32_t>(height) });
if (premultipliedImage.bytes() != uint32_t(size)) {
- throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch");
+ throw mbgl::util::StyleImageException("Image pixel count mismatch");
}
jni::GetArrayRegion(env, *pixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index d7351c7677..920779c18f 100644
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -763,7 +763,7 @@ void NativeMapView::addAnnotationIcon(JNIEnv& env, const jni::String& symbol, ji
mbgl::PremultipliedImage premultipliedImage({ static_cast<uint32_t>(w), static_cast<uint32_t>(h) });
if (premultipliedImage.bytes() != uint32_t(size)) {
- throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch");
+ throw mbgl::util::StyleImageException("Annotation icon image pixel count mismatch");
}
jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp
index 99e2b0c8ca..f141037bc7 100644
--- a/src/mbgl/sprite/sprite_parser.cpp
+++ b/src/mbgl/sprite/sprite_parser.cpp
@@ -25,7 +25,7 @@ std::unique_ptr<style::Image> createStyleImage(const std::string& id,
ratio <= 0 || ratio > 10 ||
srcX >= image.size.width || srcY >= image.size.height ||
srcX + width > image.size.width || srcY + height > image.size.height) {
- Log::Error(Event::Sprite, "Can't create sprite with invalid metrics: %ux%u@%u,%u in %ux%u@%sx sprite",
+ Log::Error(Event::Sprite, "Can't create image with invalid metrics: %ux%u@%u,%u in %ux%u@%sx sprite",
width, height, srcX, srcY,
image.size.width, image.size.height,
util::toString(ratio).c_str());
diff --git a/src/mbgl/style/image.cpp b/src/mbgl/style/image.cpp
index 1747de5fcc..269f5f9d1a 100644
--- a/src/mbgl/style/image.cpp
+++ b/src/mbgl/style/image.cpp
@@ -6,11 +6,14 @@ namespace mbgl {
namespace style {
Image::Image(std::string id,
- PremultipliedImage &&image,
+ PremultipliedImage&& image,
const float pixelRatio,
- bool sdf)
- : baseImpl(makeMutable<Impl>(std::move(id), std::move(image), pixelRatio, sdf)) {
-}
+ bool sdf,
+ ImageStretches stretchX,
+ ImageStretches stretchY,
+ optional<ImageContent> content)
+ : baseImpl(makeMutable<Impl>(
+ std::move(id), std::move(image), pixelRatio, sdf, std::move(stretchX), std::move(stretchY), content)) {}
std::string Image::getID() const {
return baseImpl->id;
@@ -30,5 +33,17 @@ float Image::getPixelRatio() const {
return baseImpl->pixelRatio;
}
+const ImageStretches& Image::getStretchX() const {
+ return baseImpl->stretchX;
+}
+
+const ImageStretches& Image::getStretchY() const {
+ return baseImpl->stretchY;
+}
+
+const ImageContent& Image::getContent() const {
+ return baseImpl->content;
+}
+
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/image_impl.cpp b/src/mbgl/style/image_impl.cpp
index ce327262e8..c43bb552ee 100644
--- a/src/mbgl/style/image_impl.cpp
+++ b/src/mbgl/style/image_impl.cpp
@@ -4,19 +4,60 @@
namespace mbgl {
namespace style {
+namespace {
+
+bool validateStretch(const ImageStretches& stretches, const float size) {
+ if (stretches.empty()) {
+ return true;
+ }
+ float last = 0;
+ for (auto& part : stretches) {
+ if (part.first < last || part.second < part.first || size < part.second) {
+ return false;
+ }
+ last = part.second;
+ }
+ return true;
+}
+
+bool validateContent(const ImageContent& content, const Size& size) {
+ if (content.left < 0 || size.width < content.left) return false;
+ if (content.top < 0 || size.height < content.top) return false;
+ if (content.right < 0 || size.width < content.right) return false;
+ if (content.bottom < 0 || size.height < content.bottom) return false;
+ if (content.right < content.left) return false;
+ if (content.bottom < content.top) return false;
+ return true;
+}
+
+} // namespace
+
Image::Impl::Impl(std::string id_,
PremultipliedImage&& image_,
const float pixelRatio_,
- bool sdf_)
- : id(std::move(id_)),
- image(std::move(image_)),
- pixelRatio(pixelRatio_),
- sdf(sdf_) {
-
+ bool sdf_,
+ ImageStretches stretchX_,
+ ImageStretches stretchY_,
+ optional<ImageContent> content_)
+ : id(std::move(id_)),
+ image(std::move(image_)),
+ pixelRatio(pixelRatio_),
+ sdf(sdf_),
+ stretchX(std::move(stretchX_)),
+ stretchY(std::move(stretchY_)),
+ content(content_
+ ? std::move(*content_)
+ : ImageContent{0, 0, static_cast<float>(image.size.width), static_cast<float>(image.size.height)}) {
if (!image.valid()) {
- throw util::SpriteImageException("Sprite image dimensions may not be zero");
+ throw util::StyleImageException("dimensions may not be zero");
} else if (pixelRatio <= 0) {
- throw util::SpriteImageException("Sprite pixelRatio may not be <= 0");
+ throw util::StyleImageException("pixelRatio may not be <= 0");
+ } else if (!validateStretch(stretchX, image.size.width)) {
+ throw util::StyleImageException("stretchX is out of bounds or overlapping");
+ } else if (!validateStretch(stretchY, image.size.height)) {
+ throw util::StyleImageException("stretchY is out of bounds or overlapping");
+ } else if (!validateContent(content, image.size)) {
+ throw util::StyleImageException("content area is invalid");
}
}
diff --git a/src/mbgl/style/image_impl.hpp b/src/mbgl/style/image_impl.hpp
index b2decbf781..683cd3347b 100644
--- a/src/mbgl/style/image_impl.hpp
+++ b/src/mbgl/style/image_impl.hpp
@@ -11,7 +11,13 @@ namespace style {
class Image::Impl {
public:
- Impl(std::string id, PremultipliedImage&&, float pixelRatio, bool sdf = false);
+ Impl(std::string id,
+ PremultipliedImage&&,
+ float pixelRatio,
+ bool sdf = false,
+ ImageStretches stretchX = {},
+ ImageStretches stretchY = {},
+ optional<ImageContent> content = nullopt);
const std::string id;
@@ -22,6 +28,13 @@ public:
// Whether this image should be interpreted as a signed distance field icon.
const bool sdf;
+
+ // Stretch areas of this image.
+ const ImageStretches stretchX;
+ const ImageStretches stretchY;
+
+ // The space where text can be fit into this image.
+ const ImageContent content;
};
} // namespace style
diff --git a/test/sprite/sprite_parser.test.cpp b/test/sprite/sprite_parser.test.cpp
index 529e4c75e8..1d6e006980 100644
--- a/test/sprite/sprite_parser.test.cpp
+++ b/test/sprite/sprite_parser.test.cpp
@@ -45,91 +45,91 @@ TEST(Sprite, SpriteImageCreationInvalid) {
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 0x16@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 0x16@0,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 16x0@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 16x0@0,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 4294967295x16@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 4294967295x16@0,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 16x4294967295@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 16x4294967295@0,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 1x1@0,0 in 200x299@0x sprite",
+ "Can't create image with invalid metrics: 1x1@0,0 in 200x299@0x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 1x1@0,0 in 200x299@-1x sprite",
+ "Can't create image with invalid metrics: 1x1@0,0 in 200x299@-1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 1x1@0,0 in 200x299@23x sprite",
+ "Can't create image with invalid metrics: 1x1@0,0 in 200x299@23x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 2048x16@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 2048x16@0,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 16x1025@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 16x1025@0,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 16x16@4294967295,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 16x16@4294967295,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 16x16@0,4294967295 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 16x16@0,4294967295 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 201x16@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 201x16@0,0 in 200x299@1x sprite",
}));
EXPECT_EQ(1u, log.count({
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 16x300@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 16x300@0,0 in 200x299@1x sprite",
}));
}
@@ -307,7 +307,7 @@ TEST(Sprite, SpriteParsingEmptyImage) {
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 0x0@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 0x0@0,0 in 200x299@1x sprite",
}));
}
@@ -340,7 +340,7 @@ TEST(Sprite, SpriteParsingWidthTooBig) {
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 0x32@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 0x32@0,0 in 200x299@1x sprite",
}));
}
@@ -363,7 +363,7 @@ TEST(Sprite, SpriteParsingNegativeWidth) {
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 0x32@0,0 in 200x299@1x sprite",
+ "Can't create image with invalid metrics: 0x32@0,0 in 200x299@1x sprite",
}));
}
@@ -380,6 +380,6 @@ TEST(Sprite, SpriteParsingNullRatio) {
EventSeverity::Error,
Event::Sprite,
int64_t(-1),
- "Can't create sprite with invalid metrics: 32x32@0,0 in 200x299@0x sprite",
+ "Can't create image with invalid metrics: 32x32@0,0 in 200x299@0x sprite",
}));
}
diff --git a/test/style/style_image.test.cpp b/test/style/style_image.test.cpp
index e49bf37582..75dafb9938 100644
--- a/test/style/style_image.test.cpp
+++ b/test/style/style_image.test.cpp
@@ -10,8 +10,8 @@ TEST(StyleImage, ZeroWidth) {
try {
style::Image("test", PremultipliedImage({ 0, 16 }), 2.0);
FAIL() << "Expected exception";
- } catch (util::SpriteImageException& ex) {
- EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what());
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("dimensions may not be zero", ex.what());
}
}
@@ -19,8 +19,8 @@ TEST(StyleImage, ZeroHeight) {
try {
style::Image("test", PremultipliedImage({ 16, 0 }), 2.0);
FAIL() << "Expected exception";
- } catch (util::SpriteImageException& ex) {
- EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what());
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("dimensions may not be zero", ex.what());
}
}
@@ -28,8 +28,8 @@ TEST(StyleImage, ZeroRatio) {
try {
style::Image("test", PremultipliedImage({ 16, 16 }), 0.0);
FAIL() << "Expected exception";
- } catch (util::SpriteImageException& ex) {
- EXPECT_STREQ("Sprite pixelRatio may not be <= 0", ex.what());
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("pixelRatio may not be <= 0", ex.what());
}
}
@@ -46,3 +46,76 @@ TEST(StyleImage, FractionalRatio) {
EXPECT_EQ(12u, image.getImage().size.height);
EXPECT_EQ(1.5, image.getPixelRatio());
}
+
+TEST(StyleImage, InvalidStretchX) {
+ // out of left bound
+ try {
+ style::Image("test", PremultipliedImage({16, 16}), 1, {{-1, 3}});
+ FAIL() << "Expected exception";
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("stretchX is out of bounds or overlapping", ex.what());
+ }
+
+ // overlapping
+ try {
+ style::Image("test", PremultipliedImage({16, 16}), 1, {{0, 3}, {2., 4.}});
+ FAIL() << "Expected exception";
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("stretchX is out of bounds or overlapping", ex.what());
+ }
+}
+
+TEST(StyleImage, InvalidStretchY) {
+ // out of bottom bound
+ try {
+ style::Image("test", PremultipliedImage({16, 16}), 1, {}, {{14, 20}});
+ FAIL() << "Expected exception";
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("stretchX is out of bounds or overlapping", ex.what());
+ }
+
+ // must be sorted
+ try {
+ style::Image("test", PremultipliedImage({16, 16}), 1, {}, {{4, 8}, {2, 3}});
+ FAIL() << "Expected exception";
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("stretchX is out of bounds or overlapping", ex.what());
+ }
+}
+
+TEST(StyleImage, InvalidContent) {
+ // bottom right out of bounds
+ try {
+ style::Image("test", PremultipliedImage({16, 16}), 1, {}, {}, style::ImageContent{0, 0, 24, 28});
+ FAIL() << "Expected exception";
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("content area is invalid", ex.what());
+ }
+
+ // bottom right < top left
+ try {
+ style::Image("test", PremultipliedImage({16, 16}), 1, {}, {}, style::ImageContent{14, 14, 12, 10});
+ FAIL() << "Expected exception";
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("content area is invalid", ex.what());
+ }
+
+ // top left out of bounds
+ try {
+ style::Image("test", PremultipliedImage({16, 16}), 1, {}, {}, style::ImageContent{-2, -8, 12, 10});
+ FAIL() << "Expected exception";
+ } catch (util::StyleImageException& ex) {
+ EXPECT_STREQ("content area is invalid", ex.what());
+ }
+}
+
+TEST(StyleImage, StretchContent) {
+ style::Image image(
+ "test", PremultipliedImage({16, 16}), 1, {{2, 14}}, {{0, 4}, {12, 16}}, style::ImageContent{2, 2, 14, 14});
+ EXPECT_EQ(16u, image.getImage().size.width);
+ EXPECT_EQ(16u, image.getImage().size.height);
+ EXPECT_EQ(1.0, image.getPixelRatio());
+ EXPECT_EQ((style::ImageStretches{{2, 14}}), image.getStretchX());
+ EXPECT_EQ((style::ImageStretches{{0, 4}, {12, 16}}), image.getStretchY());
+ EXPECT_EQ((style::ImageContent{2, 2, 14, 14}), image.getContent());
+}