[core] Use optional where SpriteAtlas return values might not exist

Fixes #3162
This commit is contained in:
John Firebaugh
2015-12-01 17:38:50 -08:00
parent e326d4972f
commit e5181e2668
7 changed files with 61 additions and 61 deletions

View File

@@ -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,

View File

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

View File

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

View File

@@ -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;
}
}

View File

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

View File

@@ -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);

View File

@@ -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);