#include #include #include #include #include #include #include #include #include #include #include #include #include using namespace mbgl; TEST(ImageManager, Missing) { ImageManager imageManager; EXPECT_FALSE(imageManager.getImage("doesnotexist")); } TEST(ImageManager, Basic) { FixtureLog log; ImageManager imageManager; auto images = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), util::read_file("test/fixtures/annotations/emerald.json")); for (auto& image : images) { imageManager.addImage(image->baseImpl); } 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) { FixtureLog log; ImageManager imageManager; imageManager.addImage(makeMutable("one", PremultipliedImage({ 16, 16 }), 2)); imageManager.addImage(makeMutable("two", PremultipliedImage({ 16, 16 }), 2)); imageManager.addImage(makeMutable("three", PremultipliedImage({ 16, 16 }), 2)); imageManager.removeImage("one"); imageManager.removeImage("two"); EXPECT_NE(nullptr, imageManager.getImage("three")); EXPECT_EQ(nullptr, imageManager.getImage("two")); EXPECT_EQ(nullptr, imageManager.getImage("four")); } TEST(ImageManager, RemoveReleasesBinPackRect) { FixtureLog log; ImageManager imageManager; imageManager.addImage(makeMutable("big", PremultipliedImage({ 32, 32 }), 1)); EXPECT_TRUE(imageManager.getImage("big")); imageManager.removeImage("big"); imageManager.addImage(makeMutable("big", PremultipliedImage({ 32, 32 }), 1)); EXPECT_TRUE(imageManager.getImage("big")); EXPECT_TRUE(log.empty()); } class StubImageRequestor : public ImageRequestor { public: StubImageRequestor(ImageManager& imageManager) : ImageRequestor(imageManager) {} void onImagesAvailable(ImageMap icons, ImageMap patterns, std::unordered_map versionMap, uint64_t imageCorrelationID_) final { if (imagesAvailable && imageCorrelationID == imageCorrelationID_) imagesAvailable(icons, patterns, versionMap); } std::function)> imagesAvailable; uint64_t imageCorrelationID = 0; }; TEST(ImageManager, NotifiesRequestorWhenSpriteIsLoaded) { util::RunLoop runLoop; ImageManager imageManager; StubImageRequestor requestor(imageManager); bool notified = false; ImageManagerObserver observer; imageManager.setObserver(&observer); requestor.imagesAvailable = [&] (ImageMap, ImageMap, std::unordered_map) { notified = true; }; uint64_t imageCorrelationID = 0; ImageDependencies dependencies; dependencies.emplace("one", ImageType::Icon); imageManager.getImages(requestor, std::make_pair(dependencies, imageCorrelationID)); runLoop.runOnce(); ASSERT_FALSE(notified); imageManager.setLoaded(true); runLoop.runOnce(); ASSERT_FALSE(notified); imageManager.notifyIfMissingImageAdded(); runLoop.runOnce(); ASSERT_TRUE(notified); } TEST(ImageManager, NotifiesRequestorImmediatelyIfDependenciesAreSatisfied) { ImageManager imageManager; StubImageRequestor requestor(imageManager); bool notified = false; requestor.imagesAvailable = [&] (ImageMap, ImageMap, std::unordered_map) { notified = true; }; uint64_t imageCorrelationID = 0; ImageDependencies dependencies; dependencies.emplace("one", ImageType::Icon); imageManager.addImage(makeMutable("one", PremultipliedImage({ 16, 16 }), 2)); imageManager.getImages(requestor, std::make_pair(dependencies, imageCorrelationID)); ASSERT_TRUE(notified); } class StubImageManagerObserver : public ImageManagerObserver { public: int count = 0; std::function imageMissing = [](const std::string&){}; virtual void onStyleImageMissing(const std::string& id, std::function done) override { count++; imageMissing(id); done(); } }; TEST(ImageManager, OnStyleImageMissingBeforeSpriteLoaded) { util::RunLoop runLoop; ImageManager imageManager; StubImageRequestor requestor(imageManager); StubImageManagerObserver observer; imageManager.setObserver(&observer); bool notified = false; requestor.imagesAvailable = [&] (ImageMap, ImageMap, std::unordered_map) { notified = true; }; uint64_t imageCorrelationID = 0; ImageDependencies dependencies; dependencies.emplace("pre", ImageType::Icon); imageManager.getImages(requestor, std::make_pair(dependencies, imageCorrelationID)); runLoop.runOnce(); EXPECT_EQ(observer.count, 0); ASSERT_FALSE(notified); imageManager.setLoaded(true); runLoop.runOnce(); EXPECT_EQ(observer.count, 1); ASSERT_FALSE(notified); imageManager.notifyIfMissingImageAdded(); runLoop.runOnce(); EXPECT_EQ(observer.count, 1); ASSERT_TRUE(notified); } TEST(ImageManager, OnStyleImageMissingAfterSpriteLoaded) { util::RunLoop runLoop; ImageManager imageManager; StubImageRequestor requestor(imageManager); StubImageManagerObserver observer; imageManager.setObserver(&observer); bool notified = false; requestor.imagesAvailable = [&] (ImageMap, ImageMap, std::unordered_map) { notified = true; }; EXPECT_EQ(observer.count, 0); ASSERT_FALSE(notified); imageManager.setLoaded(true); runLoop.runOnce(); uint64_t imageCorrelationID = 0; ImageDependencies dependencies; dependencies.emplace("after", ImageType::Icon); imageManager.getImages(requestor, std::make_pair(dependencies, imageCorrelationID)); runLoop.runOnce(); EXPECT_EQ(observer.count, 1); ASSERT_FALSE(notified); imageManager.notifyIfMissingImageAdded(); runLoop.runOnce(); EXPECT_EQ(observer.count, 1); ASSERT_TRUE(notified); } TEST(ImageManager, ReduceMemoryUsage) { util::RunLoop runLoop; ImageManager imageManager; StubImageManagerObserver observer; observer.imageMissing = [&imageManager] (const std::string& id) { imageManager.addImage(makeMutable(id, PremultipliedImage({ 16, 16 }), 1)); }; imageManager.setObserver(&observer); imageManager.setLoaded(true); runLoop.runOnce(); // Single requestor { std::unique_ptr requestor = std::make_unique(imageManager); imageManager.getImages(*requestor, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 0ull)); runLoop.runOnce(); EXPECT_EQ(observer.count, 1); ASSERT_FALSE(imageManager.getImage("missing") == nullptr); } // Reduce memory usage and check that unused image was deleted. imageManager.reduceMemoryUse(); runLoop.runOnce(); ASSERT_TRUE(imageManager.getImage("missing") == nullptr); // Multiple requestors { std::unique_ptr requestor1 = std::make_unique(imageManager); std::unique_ptr requestor2 = std::make_unique(imageManager); imageManager.getImages(*requestor1, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 0ull)); imageManager.getImages(*requestor2, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 1ull)); runLoop.runOnce(); EXPECT_EQ(observer.count, 2); ASSERT_FALSE(imageManager.getImage("missing") == nullptr); } // Reduce memory usage and check that unused image was deleted when all requestors are destructed. imageManager.reduceMemoryUse(); runLoop.runOnce(); ASSERT_TRUE(imageManager.getImage("missing") == nullptr); // Multiple requestors, check that image resource is not destroyed if there is at least 1 requestor that uses it. std::unique_ptr requestor = std::make_unique(imageManager); { std::unique_ptr requestor1 = std::make_unique(imageManager); imageManager.getImages(*requestor, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 0ull)); imageManager.getImages(*requestor1, std::make_pair(ImageDependencies{{"missing", ImageType::Icon}}, 1ull)); runLoop.runOnce(); EXPECT_EQ(observer.count, 3); ASSERT_FALSE(imageManager.getImage("missing") == nullptr); } // Reduce memory usage and check that requested image is not destructed. imageManager.reduceMemoryUse(); runLoop.runOnce(); ASSERT_FALSE(imageManager.getImage("missing") == nullptr); // Release last requestor and check if resource was released after reduceMemoryUse(). requestor.reset(); imageManager.reduceMemoryUse(); runLoop.runOnce(); ASSERT_TRUE(imageManager.getImage("missing") == nullptr); }