[core] Use optional where SpriteAtlas return values might not exist
Fixes #3162
This commit is contained in:
@@ -276,19 +276,20 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
|
||||
const BackgroundPaintProperties& properties = layer.paint;
|
||||
|
||||
if (!properties.pattern.value.to.empty()) {
|
||||
if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque))
|
||||
mapbox::util::optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true);
|
||||
mapbox::util::optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true);
|
||||
|
||||
if ((properties.opacity >= 1.0f) != (pass == RenderPass::Opaque) || !imagePosA || !imagePosB)
|
||||
return;
|
||||
|
||||
SpriteAtlasPosition imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true);
|
||||
SpriteAtlasPosition imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true);
|
||||
float zoomFraction = state.getZoomFraction();
|
||||
|
||||
config.program = patternShader->program;
|
||||
patternShader->u_matrix = identityMatrix;
|
||||
patternShader->u_pattern_tl_a = imagePosA.tl;
|
||||
patternShader->u_pattern_br_a = imagePosA.br;
|
||||
patternShader->u_pattern_tl_b = imagePosB.tl;
|
||||
patternShader->u_pattern_br_b = imagePosB.br;
|
||||
patternShader->u_pattern_tl_a = (*imagePosA).tl;
|
||||
patternShader->u_pattern_br_a = (*imagePosA).br;
|
||||
patternShader->u_pattern_tl_b = (*imagePosB).tl;
|
||||
patternShader->u_pattern_br_b = (*imagePosB).br;
|
||||
patternShader->u_mix = properties.pattern.value.t;
|
||||
patternShader->u_opacity = properties.opacity;
|
||||
|
||||
@@ -296,7 +297,7 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
|
||||
PrecisionPoint center = state.latLngToPoint(latLng);
|
||||
float scale = 1 / std::pow(2, zoomFraction);
|
||||
|
||||
std::array<float, 2> sizeA = imagePosA.size;
|
||||
std::array<float, 2> sizeA = (*imagePosA).size;
|
||||
mat3 matrixA;
|
||||
matrix::identity(matrixA);
|
||||
matrix::scale(matrixA, matrixA,
|
||||
@@ -310,7 +311,7 @@ void Painter::renderBackground(const BackgroundLayer& layer) {
|
||||
scale * state.getWidth() / 2,
|
||||
-scale * state.getHeight() / 2);
|
||||
|
||||
std::array<float, 2> sizeB = imagePosB.size;
|
||||
std::array<float, 2> sizeB = (*imagePosB).size;
|
||||
mat3 matrixB;
|
||||
matrix::identity(matrixB);
|
||||
matrix::scale(matrixB, matrixB,
|
||||
|
||||
@@ -59,30 +59,30 @@ void Painter::renderFill(FillBucket& bucket, const FillLayer& layer, const TileI
|
||||
}
|
||||
|
||||
if (pattern) {
|
||||
// Image fill.
|
||||
if (pass == RenderPass::Translucent) {
|
||||
mapbox::util::optional<SpriteAtlasPosition> posA = spriteAtlas->getPosition(properties.pattern.value.from, true);
|
||||
mapbox::util::optional<SpriteAtlasPosition> posB = spriteAtlas->getPosition(properties.pattern.value.to, true);
|
||||
|
||||
const SpriteAtlasPosition posA = spriteAtlas->getPosition(properties.pattern.value.from, true);
|
||||
const SpriteAtlasPosition posB = spriteAtlas->getPosition(properties.pattern.value.to, true);
|
||||
// Image fill.
|
||||
if (pass == RenderPass::Translucent && posA && posB) {
|
||||
float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z) / id.overscaling;
|
||||
|
||||
mat3 patternMatrixA;
|
||||
matrix::identity(patternMatrixA);
|
||||
matrix::scale(patternMatrixA, patternMatrixA,
|
||||
1.0f / (posA.size[0] * factor * properties.pattern.value.fromScale),
|
||||
1.0f / (posA.size[1] * factor * properties.pattern.value.fromScale));
|
||||
1.0f / ((*posA).size[0] * factor * properties.pattern.value.fromScale),
|
||||
1.0f / ((*posA).size[1] * factor * properties.pattern.value.fromScale));
|
||||
mat3 patternMatrixB;
|
||||
matrix::identity(patternMatrixB);
|
||||
matrix::scale(patternMatrixB, patternMatrixB,
|
||||
1.0f / (posB.size[0] * factor * properties.pattern.value.toScale),
|
||||
1.0f / (posB.size[1] * factor * properties.pattern.value.toScale));
|
||||
1.0f / ((*posB).size[0] * factor * properties.pattern.value.toScale),
|
||||
1.0f / ((*posB).size[1] * factor * properties.pattern.value.toScale));
|
||||
|
||||
config.program = patternShader->program;
|
||||
patternShader->u_matrix = vtxMatrix;
|
||||
patternShader->u_pattern_tl_a = posA.tl;
|
||||
patternShader->u_pattern_br_a = posA.br;
|
||||
patternShader->u_pattern_tl_b = posB.tl;
|
||||
patternShader->u_pattern_br_b = posB.br;
|
||||
patternShader->u_pattern_tl_a = (*posA).tl;
|
||||
patternShader->u_pattern_br_a = (*posA).br;
|
||||
patternShader->u_pattern_tl_b = (*posB).tl;
|
||||
patternShader->u_pattern_br_b = (*posB).br;
|
||||
patternShader->u_opacity = properties.opacity;
|
||||
patternShader->u_image = 0;
|
||||
patternShader->u_mix = properties.pattern.value.t;
|
||||
|
||||
@@ -102,8 +102,11 @@ void Painter::renderLine(LineBucket& bucket, const LineLayer& layer, const TileI
|
||||
bucket.drawLineSDF(*linesdfShader);
|
||||
|
||||
} else if (!properties.pattern.value.from.empty()) {
|
||||
SpriteAtlasPosition imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true);
|
||||
SpriteAtlasPosition imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true);
|
||||
mapbox::util::optional<SpriteAtlasPosition> imagePosA = spriteAtlas->getPosition(properties.pattern.value.from, true);
|
||||
mapbox::util::optional<SpriteAtlasPosition> imagePosB = spriteAtlas->getPosition(properties.pattern.value.to, true);
|
||||
|
||||
if (!imagePosA || !imagePosB)
|
||||
return;
|
||||
|
||||
float factor = 8.0 / std::pow(2, state.getIntegerZoom() - id.z) * id.overscaling;
|
||||
|
||||
@@ -115,12 +118,12 @@ void Painter::renderLine(LineBucket& bucket, const LineLayer& layer, const TileI
|
||||
linepatternShader->u_ratio = ratio;
|
||||
linepatternShader->u_blur = blur;
|
||||
|
||||
linepatternShader->u_pattern_size_a = {{imagePosA.size[0] * factor * properties.pattern.value.fromScale, imagePosA.size[1]}};
|
||||
linepatternShader->u_pattern_tl_a = imagePosA.tl;
|
||||
linepatternShader->u_pattern_br_a = imagePosA.br;
|
||||
linepatternShader->u_pattern_size_b = {{imagePosB.size[0] * factor * properties.pattern.value.toScale, imagePosB.size[1]}};
|
||||
linepatternShader->u_pattern_tl_b = imagePosB.tl;
|
||||
linepatternShader->u_pattern_br_b = imagePosB.br;
|
||||
linepatternShader->u_pattern_size_a = {{(*imagePosA).size[0] * factor * properties.pattern.value.fromScale, (*imagePosA).size[1]}};
|
||||
linepatternShader->u_pattern_tl_a = (*imagePosA).tl;
|
||||
linepatternShader->u_pattern_br_a = (*imagePosA).br;
|
||||
linepatternShader->u_pattern_size_b = {{(*imagePosB).size[0] * factor * properties.pattern.value.toScale, (*imagePosB).size[1]}};
|
||||
linepatternShader->u_pattern_tl_b = (*imagePosB).tl;
|
||||
linepatternShader->u_pattern_br_b = (*imagePosB).br;
|
||||
linepatternShader->u_fade = properties.pattern.value.t;
|
||||
linepatternShader->u_opacity = properties.opacity;
|
||||
linepatternShader->u_extra = extra;
|
||||
|
||||
@@ -247,10 +247,10 @@ void SymbolBucket::addFeatures(uintptr_t tileUID,
|
||||
// if feature has icon, get sprite atlas position
|
||||
if (feature.sprite.length()) {
|
||||
auto image = spriteAtlas.getImage(feature.sprite, false);
|
||||
if (image.pos.hasArea() && image.texture) {
|
||||
shapedIcon = shapeIcon(image.pos, layout);
|
||||
assert(image.texture);
|
||||
if (image.texture->sdf) {
|
||||
if (image) {
|
||||
shapedIcon = shapeIcon((*image).pos, layout);
|
||||
assert((*image).texture);
|
||||
if ((*image).texture->sdf) {
|
||||
sdfIcons = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,17 +47,17 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const size_t pixel_width
|
||||
return rect;
|
||||
}
|
||||
|
||||
SpriteAtlasElement SpriteAtlas::getImage(const std::string& name, const bool wrap) {
|
||||
mapbox::util::optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name, const bool wrap) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
auto rect_it = images.find({ name, wrap });
|
||||
if (rect_it != images.end()) {
|
||||
return { rect_it->second.pos, rect_it->second.texture };
|
||||
return SpriteAtlasElement { rect_it->second.pos, rect_it->second.texture };
|
||||
}
|
||||
|
||||
auto sprite = store.getSprite(name);
|
||||
if (!sprite) {
|
||||
return { Rect<dimension> { 0, 0, 0, 0 }, nullptr };
|
||||
return {};
|
||||
}
|
||||
|
||||
Rect<dimension> rect = allocateImage(sprite->width, sprite->height);
|
||||
@@ -65,19 +65,24 @@ SpriteAtlasElement SpriteAtlas::getImage(const std::string& name, const bool wra
|
||||
if (debug::spriteWarnings) {
|
||||
Log::Warning(Event::Sprite, "sprite atlas bitmap overflow");
|
||||
}
|
||||
return { Rect<dimension> { 0, 0, 0, 0 }, nullptr };
|
||||
return {};
|
||||
}
|
||||
|
||||
const Holder& holder = images.emplace(Key{ name, wrap }, Holder{ sprite, rect }).first->second;
|
||||
copy(holder, wrap);
|
||||
|
||||
return { rect, sprite };
|
||||
return SpriteAtlasElement { rect, sprite };
|
||||
}
|
||||
|
||||
SpriteAtlasPosition SpriteAtlas::getPosition(const std::string& name, bool repeating) {
|
||||
mapbox::util::optional<SpriteAtlasPosition> SpriteAtlas::getPosition(const std::string& name, bool repeating) {
|
||||
std::lock_guard<std::recursive_mutex> lock(mtx);
|
||||
|
||||
auto rect = getImage(name, repeating).pos;
|
||||
auto img = getImage(name, repeating);
|
||||
if (!img) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto rect = (*img).pos;
|
||||
if (repeating) {
|
||||
// When the image is repeating, get the correct position of the image, rather than the
|
||||
// one rounded up to 4 pixels.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/util/ptr.hpp>
|
||||
|
||||
#include <mapbox/optional.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
@@ -41,15 +43,12 @@ public:
|
||||
SpriteAtlas(dimension width, dimension height, float pixelRatio, SpriteStore& store);
|
||||
~SpriteAtlas();
|
||||
|
||||
// Returns the coordinates of an image that is sourced from the sprite image.
|
||||
// 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.
|
||||
SpriteAtlasElement getImage(const std::string& name, const bool wrap);
|
||||
// If the sprite is loaded, copies the requsted image from it into the atlas and returns
|
||||
// the resulting icon measurements. If not, returns an empty optional.
|
||||
mapbox::util::optional<SpriteAtlasElement> 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);
|
||||
mapbox::util::optional<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.
|
||||
void bind(bool linear = false);
|
||||
|
||||
@@ -29,7 +29,7 @@ TEST(Sprite, SpriteAtlas) {
|
||||
// Image hasn't been created yet.
|
||||
EXPECT_FALSE(atlas.getData());
|
||||
|
||||
auto metro = atlas.getImage("metro", false);
|
||||
auto metro = *atlas.getImage("metro", false);
|
||||
EXPECT_EQ(0, metro.pos.x);
|
||||
EXPECT_EQ(0, metro.pos.y);
|
||||
EXPECT_EQ(20, metro.pos.w);
|
||||
@@ -44,7 +44,7 @@ TEST(Sprite, SpriteAtlas) {
|
||||
|
||||
EXPECT_TRUE(atlas.getData());
|
||||
|
||||
auto pos = atlas.getPosition("metro", false);
|
||||
auto pos = *atlas.getPosition("metro", false);
|
||||
EXPECT_DOUBLE_EQ(20, pos.size[0]);
|
||||
EXPECT_DOUBLE_EQ(20, pos.size[1]);
|
||||
EXPECT_DOUBLE_EQ(1.0f / 63, pos.tl[0]);
|
||||
@@ -52,16 +52,8 @@ TEST(Sprite, SpriteAtlas) {
|
||||
EXPECT_DOUBLE_EQ(21.0f / 63, pos.br[0]);
|
||||
EXPECT_DOUBLE_EQ(21.0f / 112, pos.br[1]);
|
||||
|
||||
|
||||
auto missing = atlas.getImage("doesnotexist", false);
|
||||
EXPECT_FALSE(missing.pos.hasArea());
|
||||
EXPECT_EQ(0, missing.pos.x);
|
||||
EXPECT_EQ(0, missing.pos.y);
|
||||
EXPECT_EQ(0, missing.pos.w);
|
||||
EXPECT_EQ(0, missing.pos.h);
|
||||
EXPECT_EQ(0, missing.pos.originalW);
|
||||
EXPECT_EQ(0, missing.pos.originalH);
|
||||
EXPECT_FALSE(missing.texture);
|
||||
EXPECT_FALSE(missing);
|
||||
|
||||
EXPECT_EQ(1u, log.count({
|
||||
EventSeverity::Info,
|
||||
@@ -71,7 +63,7 @@ TEST(Sprite, SpriteAtlas) {
|
||||
}));
|
||||
|
||||
// Different wrapping mode produces different image.
|
||||
auto metro2 = atlas.getImage("metro", true);
|
||||
auto metro2 = *atlas.getImage("metro", true);
|
||||
EXPECT_EQ(20, metro2.pos.x);
|
||||
EXPECT_EQ(0, metro2.pos.y);
|
||||
EXPECT_EQ(20, metro2.pos.w);
|
||||
@@ -103,7 +95,7 @@ TEST(Sprite, SpriteAtlasSize) {
|
||||
EXPECT_EQ(89, atlas.getTextureWidth());
|
||||
EXPECT_EQ(157, atlas.getTextureHeight());
|
||||
|
||||
auto metro = atlas.getImage("metro", false);
|
||||
auto metro = *atlas.getImage("metro", false);
|
||||
EXPECT_EQ(0, metro.pos.x);
|
||||
EXPECT_EQ(0, metro.pos.y);
|
||||
EXPECT_EQ(20, metro.pos.w);
|
||||
@@ -137,7 +129,7 @@ TEST(Sprite, SpriteAtlasUpdates) {
|
||||
EXPECT_EQ(32, atlas.getTextureHeight());
|
||||
|
||||
store.setSprite("one", std::make_shared<SpriteImage>(16, 12, 1, std::string(16 * 12 * 4, '\x00')));
|
||||
auto one = atlas.getImage("one", false);
|
||||
auto one = *atlas.getImage("one", false);
|
||||
EXPECT_EQ(0, one.pos.x);
|
||||
EXPECT_EQ(0, one.pos.y);
|
||||
EXPECT_EQ(20, one.pos.w);
|
||||
|
||||
Reference in New Issue
Block a user