From a9abc43510b4206ffa9c54c3d4c9aa2ed0d1cfce Mon Sep 17 00:00:00 2001 From: Mikhail Pozdnyakov Date: Wed, 29 May 2019 16:06:56 +0300 Subject: [core] Introduce PatternAtlas --- src/core-files.json | 2 + src/mbgl/renderer/image_manager.cpp | 110 ++------------------- src/mbgl/renderer/image_manager.hpp | 53 ++-------- .../renderer/layers/render_background_layer.cpp | 23 +++-- src/mbgl/renderer/paint_parameters.cpp | 6 +- src/mbgl/renderer/paint_parameters.hpp | 7 +- src/mbgl/renderer/pattern_atlas.cpp | 109 ++++++++++++++++++++ src/mbgl/renderer/pattern_atlas.hpp | 53 ++++++++++ src/mbgl/renderer/render_layer.hpp | 2 + src/mbgl/renderer/renderer_impl.cpp | 14 ++- src/mbgl/renderer/renderer_impl.hpp | 2 + src/mbgl/tile/geometry_tile.hpp | 10 +- test/renderer/image_manager.test.cpp | 47 +-------- test/renderer/pattern_atlas.test.cpp | 82 +++++++++++++++ test/test-files.json | 1 + 15 files changed, 309 insertions(+), 212 deletions(-) create mode 100644 src/mbgl/renderer/pattern_atlas.cpp create mode 100644 src/mbgl/renderer/pattern_atlas.hpp create mode 100644 test/renderer/pattern_atlas.test.cpp diff --git a/src/core-files.json b/src/core-files.json index 2c6e521c43..ec5836c132 100644 --- a/src/core-files.json +++ b/src/core-files.json @@ -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", diff --git a/src/mbgl/renderer/image_manager.cpp b/src/mbgl/renderer/image_manager.cpp index d5fe864fa7..ff4c20eefb 100644 --- a/src/mbgl/renderer/image_manager.cpp +++ b/src/mbgl/renderer/image_manager.cpp @@ -1,16 +1,19 @@ #include -#include + #include #include -#include -#include -#include #include +#include +#include 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 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 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(shelfPack.width()), - static_cast(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_) { } diff --git a/src/mbgl/renderer/image_manager.hpp b/src/mbgl/renderer/image_manager.hpp index f20d55415b..5305358513 100644 --- a/src/mbgl/renderer/image_manager.hpp +++ b/src/mbgl/renderer/image_manager.hpp @@ -1,16 +1,9 @@ #pragma once #include -#include -#include #include -#include -#include -#include -#include - -#include +#include #include 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 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 patterns; - PremultipliedImage atlasImage; - mbgl::optional atlasTexture; - bool dirty = true; }; class ImageRequestor { diff --git a/src/mbgl/renderer/layers/render_background_layer.cpp b/src/mbgl/renderer/layers/render_background_layer.cpp index 9d78315afa..be18e2ccb2 100644 --- a/src/mbgl/renderer/layers/render_background_layer.cpp +++ b/src/mbgl/renderer/layers/render_background_layer.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -90,8 +91,8 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) { const auto& evaluated = static_cast(*evaluatedProperties).evaluated; const auto& crossfade = static_cast(*evaluatedProperties).crossfade; if (!evaluated.get().to.empty()) { - optional imagePosA = parameters.imageManager.getPattern(evaluated.get().from); - optional imagePosB = parameters.imageManager.getPattern(evaluated.get().to); + optional imagePosA = parameters.patternAtlas.getPattern(evaluated.get().from); + optional imagePosB = parameters.patternAtlas.getPattern(evaluated.get().to); if (!imagePosA || !imagePosB) return; @@ -102,7 +103,7 @@ void RenderBackgroundLayer::render(PaintParameters& parameters) { BackgroundPatternProgram::layoutUniformValues( parameters.matrixForTile(tileID), evaluated.get(), - 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 RenderBackgroundLayer::getSolidBackground() const { return { evaluated.get() * evaluated.get() }; } +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(*evaluatedProperties).evaluated; if (!evaluated.get().to.empty()) { // Ensures that the texture gets added and uploaded to the atlas. - params.imageManager.getPattern(evaluated.get().from); - params.imageManager.getPattern(evaluated.get().to); + addPatternIfNeeded(evaluated.get().from, params); + addPatternIfNeeded(evaluated.get().to, params); } } diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 2e7cae4d3a..f4498f66b1 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -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), diff --git a/src/mbgl/renderer/paint_parameters.hpp b/src/mbgl/renderer/paint_parameters.hpp index e57e49a615..89084e9e52 100644 --- a/src/mbgl/renderer/paint_parameters.hpp +++ b/src/mbgl/renderer/paint_parameters.hpp @@ -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; diff --git a/src/mbgl/renderer/pattern_atlas.cpp b/src/mbgl/renderer/pattern_atlas.cpp new file mode 100644 index 0000000000..320412685b --- /dev/null +++ b/src/mbgl/renderer/pattern_atlas.cpp @@ -0,0 +1,109 @@ +#include +#include +#include + +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 PatternAtlas::getPattern(const std::string& id) const { + auto it = patterns.find(id); + if (it != patterns.end()) { + return it->second.position; + } + return nullopt; +} + +optional 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(shelfPack.width()), + static_cast(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 diff --git a/src/mbgl/renderer/pattern_atlas.hpp b/src/mbgl/renderer/pattern_atlas.hpp new file mode 100644 index 0000000000..f39ba25233 --- /dev/null +++ b/src/mbgl/renderer/pattern_atlas.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +namespace mbgl { + +template +class Actor; + +namespace gfx { +class UploadPass; +} // namespace gfx + +class PatternAtlas { +public: + PatternAtlas(); + PatternAtlas(const PatternAtlas&) = delete; + PatternAtlas& operator=(const PatternAtlas&) = delete; + ~PatternAtlas(); + + optional getPattern(const std::string&) const; + optional 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 patterns; + PremultipliedImage atlasImage; + mbgl::optional atlasTexture; + bool dirty = true; +}; + +} // namespace mbgl diff --git a/src/mbgl/renderer/render_layer.hpp b/src/mbgl/renderer/render_layer.hpp index 887acdfb59..94d7384ba4 100644 --- a/src/mbgl/renderer/render_layer.hpp +++ b/src/mbgl/renderer/render_layer.hpp @@ -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; }; diff --git a/src/mbgl/renderer/renderer_impl.cpp b/src/mbgl/renderer/renderer_impl.cpp index 993a912535..96ba8d7554 100644 --- a/src/mbgl/renderer/renderer_impl.cpp +++ b/src/mbgl/renderer/renderer_impl.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,7 @@ Renderer::Impl::Impl(gfx::RendererBackend& backend_, , glyphManager(std::make_unique(std::make_unique(localFontFamily))) , imageManager(std::make_unique()) , lineAtlas(std::make_unique(Size{ 256, 512 })) + , patternAtlas(std::make_unique()) , imageImpls(makeMutable>>()) , sourceImpls(makeMutable>>()) , layerImpls(makeMutable>>()) @@ -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 ------------------------------------------------------------------------------------- diff --git a/src/mbgl/renderer/renderer_impl.hpp b/src/mbgl/renderer/renderer_impl.hpp index 7bcbdbbf7c..fae79b6d52 100644 --- a/src/mbgl/renderer/renderer_impl.hpp +++ b/src/mbgl/renderer/renderer_impl.hpp @@ -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; std::unique_ptr imageManager; std::unique_ptr lineAtlas; + std::unique_ptr patternAtlas; std::unique_ptr staticData; Immutable>> imageImpls; diff --git a/src/mbgl/tile/geometry_tile.hpp b/src/mbgl/tile/geometry_tile.hpp index 768948a82d..cbc94deed0 100644 --- a/src/mbgl/tile/geometry_tile.hpp +++ b/src/mbgl/tile/geometry_tile.hpp @@ -1,12 +1,14 @@ #pragma once -#include -#include +#include +#include +#include #include #include +#include +#include #include -#include -#include +#include #include #include diff --git a/test/renderer/image_manager.test.cpp b/test/renderer/image_manager.test.cpp index 443a8539b6..0b72578c35 100644 --- a/test/renderer/image_manager.test.cpp +++ b/test/renderer/image_manager.test.cpp @@ -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("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("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) { diff --git a/test/renderer/pattern_atlas.test.cpp b/test/renderer/pattern_atlas.test.cpp new file mode 100644 index 0000000000..2a19e463bd --- /dev/null +++ b/test/renderer/pattern_atlas.test.cpp @@ -0,0 +1,82 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +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("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("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()); +} diff --git a/test/test-files.json b/test/test-files.json index 6ed78d4e61..e46f833269 100644 --- a/test/test-files.json +++ b/test/test-files.json @@ -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", -- cgit v1.2.1