[core] Introduce PatternAtlas

This commit is contained in:
Mikhail Pozdnyakov
2019-05-29 16:06:56 +03:00
parent c0e5632001
commit a9abc43510
15 changed files with 310 additions and 213 deletions

View File

@@ -124,6 +124,7 @@
"src/mbgl/renderer/layers/render_raster_layer.cpp",
"src/mbgl/renderer/layers/render_symbol_layer.cpp",
"src/mbgl/renderer/paint_parameters.cpp",
"src/mbgl/renderer/pattern_atlas.cpp",
"src/mbgl/renderer/render_layer.cpp",
"src/mbgl/renderer/render_light.cpp",
"src/mbgl/renderer/render_source.cpp",
@@ -636,6 +637,7 @@
"mbgl/renderer/paint_parameters.hpp": "src/mbgl/renderer/paint_parameters.hpp",
"mbgl/renderer/paint_property_binder.hpp": "src/mbgl/renderer/paint_property_binder.hpp",
"mbgl/renderer/paint_property_statistics.hpp": "src/mbgl/renderer/paint_property_statistics.hpp",
"mbgl/renderer/pattern_atlas.hpp": "src/mbgl/renderer/pattern_atlas.hpp",
"mbgl/renderer/possibly_evaluated_property_value.hpp": "src/mbgl/renderer/possibly_evaluated_property_value.hpp",
"mbgl/renderer/property_evaluation_parameters.hpp": "src/mbgl/renderer/property_evaluation_parameters.hpp",
"mbgl/renderer/property_evaluator.hpp": "src/mbgl/renderer/property_evaluator.hpp",

View File

@@ -1,16 +1,19 @@
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/actor/actor.hpp>
#include <mbgl/actor/scheduler.hpp>
#include <mbgl/util/logging.hpp>
#include <mbgl/gfx/upload_pass.hpp>
#include <mbgl/gfx/context.hpp>
#include <mbgl/renderer/image_manager_observer.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/logging.hpp>
namespace mbgl {
static ImageManagerObserver nullObserver;
ImageManager::ImageManager() = default;
ImageManager::~ImageManager() = default;
void ImageManager::setObserver(ImageManagerObserver* observer_) {
observer = observer_ ? observer_ : &nullObserver;
}
@@ -62,7 +65,6 @@ bool ImageManager::updateImage(Immutable<style::Image::Impl> image_) {
updatedImageVersions[image_->id]++;
}
removePattern(image_->id);
oldImage->second = std::move(image_);
return sizeChanged;
@@ -79,22 +81,6 @@ void ImageManager::removeImage(const std::string& id) {
requestedImages.erase(requestedIt);
}
images.erase(it);
removePattern(id);
}
void ImageManager::removePattern(const std::string& id) {
auto it = patterns.find(id);
if (it != patterns.end()) {
// Clear pattern from the atlas image.
const uint32_t x = it->second.bin->x;
const uint32_t y = it->second.bin->y;
const uint32_t w = it->second.bin->w;
const uint32_t h = it->second.bin->h;
PremultipliedImage::clear(atlasImage, { x, y }, { w, h });
shelfPack.unref(*it->second.bin);
patterns.erase(it);
}
}
const style::Image::Impl* ImageManager::getImage(const std::string& id) const {
@@ -243,88 +229,6 @@ void ImageManager::dumpDebugLogs() const {
Log::Info(Event::General, "ImageManager::loaded: %d", loaded);
}
// When copied into the atlas texture, image data is padded by one pixel on each side. Icon
// images are padded with fully transparent pixels, while pattern images are padded with a
// copy of the image data wrapped from the opposite side. In both cases, this ensures the
// correct behavior of GL_LINEAR texture sampling mode.
static constexpr uint16_t padding = 1;
static mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() {
mapbox::ShelfPack::ShelfPackOptions options;
options.autoResize = true;
return options;
}
ImageManager::ImageManager()
: shelfPack(64, 64, shelfPackOptions()) {
}
ImageManager::~ImageManager() = default;
optional<ImagePosition> ImageManager::getPattern(const std::string& id) {
auto it = patterns.find(id);
if (it != patterns.end()) {
return it->second.position;
}
const style::Image::Impl* image = getImage(id);
if (!image) {
return {};
}
const uint16_t width = image->image.size.width + padding * 2;
const uint16_t height = image->image.size.height + padding * 2;
mapbox::Bin* bin = shelfPack.packOne(-1, width, height);
if (!bin) {
return {};
}
atlasImage.resize(getPixelSize());
const PremultipliedImage& src = image->image;
const uint32_t x = bin->x + padding;
const uint32_t y = bin->y + padding;
const uint32_t w = src.size.width;
const uint32_t h = src.size.height;
PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h });
// Add 1 pixel wrapped padding on each side of the image.
PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T
PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B
PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L
PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R
dirty = true;
return patterns.emplace(id, Pattern { bin, { *bin, *image } }).first->second.position;
}
Size ImageManager::getPixelSize() const {
return Size {
static_cast<uint32_t>(shelfPack.width()),
static_cast<uint32_t>(shelfPack.height())
};
}
void ImageManager::upload(gfx::UploadPass& uploadPass) {
if (!atlasTexture) {
atlasTexture = uploadPass.createTexture(atlasImage);
} else if (dirty) {
uploadPass.updateTexture(*atlasTexture, atlasImage);
}
dirty = false;
}
gfx::TextureBinding ImageManager::textureBinding() {
assert(atlasTexture);
assert(!dirty);
return { atlasTexture->getResource(), gfx::TextureFilterType::Linear };
}
ImageRequestor::ImageRequestor(ImageManager& imageManager_) : imageManager(imageManager_) {
}

View File

@@ -1,16 +1,9 @@
#pragma once
#include <mbgl/style/image_impl.hpp>
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/immutable.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/gfx/texture.hpp>
#include <mbgl/renderer/image_manager_observer.hpp>
#include <mapbox/shelf-pack.hpp>
#include <set>
#include <map>
#include <string>
namespace mbgl {
@@ -22,21 +15,17 @@ namespace gfx {
class UploadPass;
} // namespace gfx
class ImageManagerObserver;
class ImageRequestor;
/*
ImageManager does two things:
1. Tracks requests for icon images from tile workers and sends responses when the requests are fulfilled.
2. Builds a texture atlas for pattern images.
These are disparate responsibilities and should eventually be handled by different classes. When we implement
data-driven support for `*-pattern`, we'll likely use per-bucket pattern atlases, and that would be a good time
to refactor this.
*/
class ImageManager : public util::noncopyable {
/**
* @brief tracks requests for icon images from tile workers and sends responses when the requests are fulfilled.
*/
class ImageManager {
public:
ImageManager();
ImageManager(const ImageManager&) = delete;
ImageManager& operator=(const ImageManager&) = delete;
~ImageManager();
void setObserver(ImageManagerObserver*);
@@ -80,32 +69,6 @@ private:
ImageMap images;
ImageManagerObserver* observer = nullptr;
// Pattern stuff
public:
optional<ImagePosition> getPattern(const std::string& name);
gfx::TextureBinding textureBinding();
void upload(gfx::UploadPass&);
Size getPixelSize() const;
// Only for use in tests.
const PremultipliedImage& getAtlasImage() const {
return atlasImage;
}
private:
struct Pattern {
mapbox::Bin* bin;
ImagePosition position;
};
mapbox::ShelfPack shelfPack;
std::unordered_map<std::string, Pattern> patterns;
PremultipliedImage atlasImage;
mbgl::optional<gfx::Texture> atlasTexture;
bool dirty = true;
};
class ImageRequestor {

View File

@@ -3,6 +3,7 @@
#include <mbgl/renderer/bucket.hpp>
#include <mbgl/renderer/upload_parameters.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/pattern_atlas.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/programs/programs.hpp>
@@ -90,8 +91,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) {
const auto& evaluated = static_cast<const BackgroundLayerProperties&>(*evaluatedProperties).evaluated;
const auto& crossfade = static_cast<const BackgroundLayerProperties&>(*evaluatedProperties).crossfade;
if (!evaluated.get<BackgroundPattern>().to.empty()) {
optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().from);
optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<BackgroundPattern>().to);
optional<ImagePosition> imagePosA = parameters.patternAtlas.getPattern(evaluated.get<BackgroundPattern>().from);
optional<ImagePosition> imagePosB = parameters.patternAtlas.getPattern(evaluated.get<BackgroundPattern>().to);
if (!imagePosA || !imagePosB)
return;
@@ -102,7 +103,7 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) {
BackgroundPatternProgram::layoutUniformValues(
parameters.matrixForTile(tileID),
evaluated.get<BackgroundOpacity>(),
parameters.imageManager.getPixelSize(),
parameters.patternAtlas.getPixelSize(),
*imagePosA,
*imagePosB,
crossfade,
@@ -110,7 +111,7 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) {
parameters.state
),
BackgroundPatternProgram::TextureBindings{
textures::image::Value{ parameters.imageManager.textureBinding() },
textures::image::Value{ parameters.patternAtlas.textureBinding() },
},
tileID
);
@@ -140,12 +141,22 @@ optional<Color> RenderBackgroundLayer::getSolidBackground() const {
return { evaluated.get<BackgroundColor>() * evaluated.get<BackgroundOpacity>() };
}
namespace {
void addPatternIfNeeded(const std::string& id, const LayerPrepareParameters& params) {
if (!params.patternAtlas.getPattern(id)) {
if (auto* image = params.imageManager.getImage(id)) {
params.patternAtlas.addPattern(*image);
}
}
}
} // namespace
void RenderBackgroundLayer::prepare(const LayerPrepareParameters& params) {
const auto& evaluated = static_cast<const BackgroundLayerProperties&>(*evaluatedProperties).evaluated;
if (!evaluated.get<BackgroundPattern>().to.empty()) {
// Ensures that the texture gets added and uploaded to the atlas.
params.imageManager.getPattern(evaluated.get<BackgroundPattern>().from);
params.imageManager.getPattern(evaluated.get<BackgroundPattern>().to);
addPatternIfNeeded(evaluated.get<BackgroundPattern>().from, params);
addPatternIfNeeded(evaluated.get<BackgroundPattern>().to, params);
}
}

View File

@@ -32,8 +32,8 @@ PaintParameters::PaintParameters(gfx::Context& context_,
const EvaluatedLight& evaluatedLight_,
const TransformParameters& transformParams_,
RenderStaticData& staticData_,
ImageManager& imageManager_,
LineAtlas& lineAtlas_)
LineAtlas& lineAtlas_,
PatternAtlas& patternAtlas_)
: context(context_),
backend(backend_),
encoder(context.createCommandEncoder()),
@@ -41,8 +41,8 @@ PaintParameters::PaintParameters(gfx::Context& context_,
evaluatedLight(evaluatedLight_),
transformParams(transformParams_),
staticData(staticData_),
imageManager(imageManager_),
lineAtlas(lineAtlas_),
patternAtlas(patternAtlas_),
mapMode(updateParameters.mode),
debugOptions(updateParameters.debugOptions),
timePoint(updateParameters.timePoint),

View File

@@ -20,6 +20,7 @@ class Programs;
class TransformState;
class ImageManager;
class LineAtlas;
class PatternAtlas;
class UnwrappedTileID;
class RenderSource;
class RenderTile;
@@ -50,8 +51,8 @@ public:
const EvaluatedLight&,
const TransformParameters&,
RenderStaticData&,
ImageManager&,
LineAtlas&);
LineAtlas&,
PatternAtlas&);
~PaintParameters();
gfx::Context& context;
@@ -64,8 +65,8 @@ public:
const TransformParameters& transformParams;
RenderStaticData& staticData;
ImageManager& imageManager;
LineAtlas& lineAtlas;
PatternAtlas& patternAtlas;
RenderPass pass = RenderPass::Opaque;
MapMode mapMode;

View File

@@ -0,0 +1,109 @@
#include <mbgl/renderer/pattern_atlas.hpp>
#include <mbgl/gfx/upload_pass.hpp>
#include <mbgl/gfx/context.hpp>
namespace mbgl {
namespace {
// When copied into the atlas texture, image data is padded by one pixel on each side. Icon
// images are padded with fully transparent pixels, while pattern images are padded with a
// copy of the image data wrapped from the opposite side. In both cases, this ensures the
// correct behavior of GL_LINEAR texture sampling mode.
const uint16_t padding = 1;
mapbox::ShelfPack::ShelfPackOptions shelfPackOptions() {
mapbox::ShelfPack::ShelfPackOptions options;
options.autoResize = true;
return options;
}
} // namespace
PatternAtlas::PatternAtlas()
: shelfPack(64, 64, shelfPackOptions()) {
}
PatternAtlas::~PatternAtlas() = default;
optional<ImagePosition> PatternAtlas::getPattern(const std::string& id) const {
auto it = patterns.find(id);
if (it != patterns.end()) {
return it->second.position;
}
return nullopt;
}
optional<ImagePosition> PatternAtlas::addPattern(const style::Image::Impl& image) {
if (patterns.find(image.id) != patterns.end()) {
return nullopt;
}
const uint16_t width = image.image.size.width + padding * 2;
const uint16_t height = image.image.size.height + padding * 2;
mapbox::Bin* bin = shelfPack.packOne(-1, width, height);
if (!bin) {
return nullopt;
}
atlasImage.resize(getPixelSize());
const PremultipliedImage& src = image.image;
const uint32_t x = bin->x + padding;
const uint32_t y = bin->y + padding;
const uint32_t w = src.size.width;
const uint32_t h = src.size.height;
PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y }, { w, h });
// Add 1 pixel wrapped padding on each side of the image.
PremultipliedImage::copy(src, atlasImage, { 0, h - 1 }, { x, y - 1 }, { w, 1 }); // T
PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x, y + h }, { w, 1 }); // B
PremultipliedImage::copy(src, atlasImage, { w - 1, 0 }, { x - 1, y }, { 1, h }); // L
PremultipliedImage::copy(src, atlasImage, { 0, 0 }, { x + w, y }, { 1, h }); // R
dirty = true;
return patterns.emplace(image.id, Pattern { bin, { *bin, image } }).first->second.position;
}
void PatternAtlas::removePattern(const std::string& id) {
auto it = patterns.find(id);
if (it != patterns.end()) {
// Clear pattern from the atlas image.
const uint32_t x = it->second.bin->x;
const uint32_t y = it->second.bin->y;
const uint32_t w = it->second.bin->w;
const uint32_t h = it->second.bin->h;
PremultipliedImage::clear(atlasImage, { x, y }, { w, h });
shelfPack.unref(*it->second.bin);
patterns.erase(it);
}
}
Size PatternAtlas::getPixelSize() const {
return {
static_cast<uint32_t>(shelfPack.width()),
static_cast<uint32_t>(shelfPack.height())
};
}
void PatternAtlas::upload(gfx::UploadPass& uploadPass) {
if (!atlasTexture) {
atlasTexture = uploadPass.createTexture(atlasImage);
} else if (dirty) {
uploadPass.updateTexture(*atlasTexture, atlasImage);
}
dirty = false;
}
gfx::TextureBinding PatternAtlas::textureBinding() const {
assert(atlasTexture);
assert(!dirty);
return { atlasTexture->getResource(), gfx::TextureFilterType::Linear };
}
} // namespace mbgl

View File

@@ -0,0 +1,53 @@
#pragma once
#include <mapbox/shelf-pack.hpp>
#include <mbgl/gfx/texture.hpp>
#include <mbgl/renderer/image_atlas.hpp>
#include <mbgl/style/image_impl.hpp>
#include <mbgl/util/optional.hpp>
#include <unordered_map>
#include <string>
namespace mbgl {
template <class T>
class Actor;
namespace gfx {
class UploadPass;
} // namespace gfx
class PatternAtlas {
public:
PatternAtlas();
PatternAtlas(const PatternAtlas&) = delete;
PatternAtlas& operator=(const PatternAtlas&) = delete;
~PatternAtlas();
optional<ImagePosition> getPattern(const std::string&) const;
optional<ImagePosition> addPattern(const style::Image::Impl&);
void removePattern(const std::string&);
gfx::TextureBinding textureBinding() const;
void upload(gfx::UploadPass&);
Size getPixelSize() const;
const PremultipliedImage& getAtlasImageForTests() const {
return atlasImage;
}
private:
struct Pattern {
mapbox::Bin* bin;
ImagePosition position;
};
mapbox::ShelfPack shelfPack;
std::unordered_map<std::string, Pattern> patterns;
PremultipliedImage atlasImage;
mbgl::optional<gfx::Texture> atlasTexture;
bool dirty = true;
};
} // namespace mbgl

View File

@@ -18,6 +18,7 @@ class PaintParameters;
class RenderSource;
class RenderTile;
class TransformState;
class PatternAtlas;
class LayerRenderData {
public:
@@ -35,6 +36,7 @@ class LayerPrepareParameters {
public:
RenderSource* source;
ImageManager& imageManager;
PatternAtlas& patternAtlas;
const TransformState& state;
};

View File

@@ -7,6 +7,7 @@
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/renderer/update_parameters.hpp>
#include <mbgl/renderer/upload_parameters.hpp>
#include <mbgl/renderer/pattern_atlas.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/transition_parameters.hpp>
#include <mbgl/renderer/property_evaluation_parameters.hpp>
@@ -52,6 +53,7 @@ Renderer::Impl::Impl(gfx::RendererBackend& backend_,
, glyphManager(std::make_unique<GlyphManager>(std::make_unique<LocalGlyphRasterizer>(localFontFamily)))
, imageManager(std::make_unique<ImageManager>())
, lineAtlas(std::make_unique<LineAtlas>(Size{ 256, 512 }))
, patternAtlas(std::make_unique<PatternAtlas>())
, imageImpls(makeMutable<std::vector<Immutable<style::Image::Impl>>>())
, sourceImpls(makeMutable<std::vector<Immutable<style::Source::Impl>>>())
, layerImpls(makeMutable<std::vector<Immutable<style::Layer::Impl>>>())
@@ -143,6 +145,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
// Remove removed images from sprite atlas.
for (const auto& entry : imageDiff.removed) {
imageManager->removeImage(entry.first);
patternAtlas->removePattern(entry.first);
}
// Add added images to sprite atlas.
@@ -152,7 +155,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
// Update changed images.
for (const auto& entry : imageDiff.changed) {
hasImageDiff = imageManager->updateImage(entry.second.after) || hasImageDiff;
if (imageManager->updateImage(entry.second.after)) {
patternAtlas->removePattern(entry.first);
hasImageDiff = true;
}
}
imageManager->notifyIfMissingImageAdded();
@@ -302,7 +308,7 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
for (auto& renderItem : renderItems) {
RenderLayer& renderLayer = renderItem.layer;
renderLayer.prepare({renderItem.source, *imageManager, updateParameters.transformState});
renderLayer.prepare({renderItem.source, *imageManager, *patternAtlas, updateParameters.transformState});
if (renderLayer.needsPlacement()) {
layersNeedPlacement.emplace_back(renderLayer);
}
@@ -362,8 +368,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
renderLight.getEvaluated(),
transformParams,
*staticData,
*imageManager,
*lineAtlas,
*patternAtlas
};
parameters.symbolFadeChange = placement->symbolFadeChange(updateParameters.timePoint);
@@ -393,8 +399,8 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
}
staticData->upload(*uploadPass);
imageManager->upload(*uploadPass);
lineAtlas->upload(*uploadPass);
patternAtlas->upload(*uploadPass);
}
// - 3D PASS -------------------------------------------------------------------------------------

View File

@@ -29,6 +29,7 @@ class SourceQueryOptions;
class GlyphManager;
class ImageManager;
class LineAtlas;
class PatternAtlas;
class CrossTileSymbolIndex;
namespace gfx {
@@ -116,6 +117,7 @@ private:
std::unique_ptr<GlyphManager> glyphManager;
std::unique_ptr<ImageManager> imageManager;
std::unique_ptr<LineAtlas> lineAtlas;
std::unique_ptr<PatternAtlas> patternAtlas;
std::unique_ptr<RenderStaticData> staticData;
Immutable<std::vector<Immutable<style::Image::Impl>>> imageImpls;

View File

@@ -1,12 +1,14 @@
#pragma once
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/actor/actor.hpp>
#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/gfx/texture.hpp>
#include <mbgl/renderer/image_manager.hpp>
#include <mbgl/text/glyph_manager.hpp>
#include <mbgl/tile/tile.hpp>
#include <mbgl/tile/geometry_tile_worker.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/optional.hpp>
#include <atomic>
#include <memory>

View File

@@ -28,51 +28,10 @@ TEST(ImageManager, Basic) {
util::read_file("test/fixtures/annotations/emerald.json"));
for (auto& image : images) {
imageManager.addImage(image->baseImpl);
auto* stored = imageManager.getImage(image->getID());
ASSERT_TRUE(stored);
EXPECT_EQ(image->getImage().size, stored->image.size);
}
auto metro = *imageManager.getPattern("metro");
EXPECT_EQ(1, metro.tl()[0]);
EXPECT_EQ(1, metro.tl()[1]);
EXPECT_EQ(19, metro.br()[0]);
EXPECT_EQ(19, metro.br()[1]);
EXPECT_EQ(18, metro.displaySize()[0]);
EXPECT_EQ(18, metro.displaySize()[1]);
EXPECT_EQ(1.0f, metro.pixelRatio);
EXPECT_EQ(imageManager.getPixelSize(), imageManager.getAtlasImage().size);
test::checkImage("test/fixtures/image_manager/basic", imageManager.getAtlasImage());
}
TEST(ImageManager, Updates) {
ImageManager imageManager;
PremultipliedImage imageA({ 16, 12 });
imageA.fill(255);
imageManager.addImage(makeMutable<style::Image::Impl>("one", std::move(imageA), 1));
auto a = *imageManager.getPattern("one");
EXPECT_EQ(1, a.tl()[0]);
EXPECT_EQ(1, a.tl()[1]);
EXPECT_EQ(17, a.br()[0]);
EXPECT_EQ(13, a.br()[1]);
EXPECT_EQ(16, a.displaySize()[0]);
EXPECT_EQ(12, a.displaySize()[1]);
EXPECT_EQ(1.0f, a.pixelRatio);
test::checkImage("test/fixtures/image_manager/updates_before", imageManager.getAtlasImage());
PremultipliedImage imageB({ 5, 5 });
imageA.fill(200);
imageManager.updateImage(makeMutable<style::Image::Impl>("one", std::move(imageB), 1));
auto b = *imageManager.getPattern("one");
EXPECT_EQ(1, b.tl()[0]);
EXPECT_EQ(1, b.tl()[1]);
EXPECT_EQ(6, b.br()[0]);
EXPECT_EQ(6, b.br()[1]);
EXPECT_EQ(5, b.displaySize()[0]);
EXPECT_EQ(5, b.displaySize()[1]);
EXPECT_EQ(1.0f, b.pixelRatio);
test::checkImage("test/fixtures/image_manager/updates_after", imageManager.getAtlasImage());
}
TEST(ImageManager, AddRemove) {

View File

@@ -0,0 +1,82 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/test/stub_style_observer.hpp>
#include <mbgl/renderer/pattern_atlas.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/style/image_impl.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/string.hpp>
#include <utility>
using namespace mbgl;
TEST(PatternAtlas, Basic) {
FixtureLog log;
PatternAtlas patternAtlas;
auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"),
util::read_file("test/fixtures/annotations/emerald.json"));
for (auto& image : images) {
if (image->getID() == "metro") {
ASSERT_TRUE(patternAtlas.addPattern(*image->baseImpl));
}
}
auto found = patternAtlas.getPattern("metro");
ASSERT_TRUE(found);
auto metro = *found;
EXPECT_EQ(1, metro.tl()[0]);
EXPECT_EQ(1, metro.tl()[1]);
EXPECT_EQ(19, metro.br()[0]);
EXPECT_EQ(19, metro.br()[1]);
EXPECT_EQ(18, metro.displaySize()[0]);
EXPECT_EQ(18, metro.displaySize()[1]);
EXPECT_EQ(1.0f, metro.pixelRatio);
EXPECT_EQ(patternAtlas.getPixelSize(), patternAtlas.getAtlasImageForTests().size);
test::checkImage("test/fixtures/image_manager/basic", patternAtlas.getAtlasImageForTests());
}
TEST(PatternAtlas, Updates) {
PatternAtlas patternAtlas;
PremultipliedImage imageA({ 16, 12 });
imageA.fill(255);
auto added = patternAtlas.addPattern(*makeMutable<style::Image::Impl>("one", std::move(imageA), 1));
ASSERT_TRUE(added);
auto found = patternAtlas.getPattern("one");
ASSERT_TRUE(found);
EXPECT_EQ(added->textureRect, found->textureRect);
auto a = *found;
EXPECT_EQ(1, a.tl()[0]);
EXPECT_EQ(1, a.tl()[1]);
EXPECT_EQ(17, a.br()[0]);
EXPECT_EQ(13, a.br()[1]);
EXPECT_EQ(16, a.displaySize()[0]);
EXPECT_EQ(12, a.displaySize()[1]);
EXPECT_EQ(1.0f, a.pixelRatio);
test::checkImage("test/fixtures/image_manager/updates_before", patternAtlas.getAtlasImageForTests());
auto imageB = makeMutable<style::Image::Impl>("one", PremultipliedImage({ 5, 5 }), 1);
EXPECT_FALSE(patternAtlas.addPattern(*imageB)); // Already added.
patternAtlas.removePattern("one");
ASSERT_FALSE(patternAtlas.getPattern("one"));
EXPECT_TRUE(patternAtlas.addPattern(*imageB));
auto b = *patternAtlas.getPattern("one");
EXPECT_EQ(1, b.tl()[0]);
EXPECT_EQ(1, b.tl()[1]);
EXPECT_EQ(6, b.br()[0]);
EXPECT_EQ(6, b.br()[1]);
EXPECT_EQ(5, b.displaySize()[0]);
EXPECT_EQ(5, b.displaySize()[1]);
EXPECT_EQ(1.0f, b.pixelRatio);
test::checkImage("test/fixtures/image_manager/updates_after", patternAtlas.getAtlasImageForTests());
}

View File

@@ -26,6 +26,7 @@
"test/programs/symbol_program.test.cpp",
"test/renderer/backend_scope.test.cpp",
"test/renderer/image_manager.test.cpp",
"test/renderer/pattern_atlas.test.cpp",
"test/sprite/sprite_loader.test.cpp",
"test/sprite/sprite_parser.test.cpp",
"test/src/mbgl/test/fixture_log_observer.cpp",