[core] Split style image collection from SpriteAtlas

This commit is contained in:
Ivo van Dongen
2017-05-12 23:19:00 +03:00
committed by John Firebaugh
parent c80f3e9d29
commit cc9f040a2d
33 changed files with 677 additions and 413 deletions

View File

@@ -512,7 +512,7 @@ TEST(Map, GetImage) {
map.setStyleJSON(util::read_file("test/fixtures/api/icon_style.json"));
map.addImage("test-icon", std::move(image));
test::checkImage("test/fixtures/map/get_icon", map.getImage("test-icon")->image);
test::checkImage("test/fixtures/map/get_icon", map.getImage("test-icon")->getImage());
}
TEST(Map, DontLoadUnneededTiles) {

View File

@@ -3,7 +3,6 @@
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/test/stub_style_observer.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/sprite/sprite_atlas.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/util/io.hpp>
@@ -185,189 +184,3 @@ TEST(SpriteAtlas, RemoveReleasesBinPackRect) {
EXPECT_TRUE(atlas.getIcon("big"));
EXPECT_TRUE(log.empty());
}
TEST(SpriteAtlas, OtherPixelRatio) {
FixtureLog log;
SpriteAtlas atlas({ 32, 32 }, 1);
// Adding mismatched sprite image
atlas.addImage("one", std::make_unique<style::Image>(PremultipliedImage({ 8, 8 }), 2));
}
TEST(SpriteAtlas, Replace) {
FixtureLog log;
SpriteAtlas atlas({ 32, 32 }, 1);
atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
auto image = atlas.getImage("sprite");
atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
EXPECT_NE(image, atlas.getImage("sprite"));
}
TEST(SpriteAtlas, ReplaceWithDifferentDimensions) {
FixtureLog log;
SpriteAtlas atlas({ 32, 32 }, 1);
atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
atlas.addImage("sprite", std::make_unique<style::Image>(PremultipliedImage({ 18, 18 }), 2));
EXPECT_EQ(1u, log.count({
EventSeverity::Warning,
Event::Sprite,
int64_t(-1),
"Can't change sprite dimensions for 'sprite'",
}));
}
class SpriteAtlasTest {
public:
SpriteAtlasTest() = default;
util::RunLoop loop;
StubFileSource fileSource;
StubStyleObserver observer;
ThreadPool threadPool { 1 };
SpriteAtlas spriteAtlas{ { 32, 32 }, 1 };
void run() {
// Squelch logging.
Log::setObserver(std::make_unique<Log::NullObserver>());
spriteAtlas.setObserver(&observer);
spriteAtlas.load("test/fixtures/resources/sprite", threadPool, fileSource);
loop.run();
}
void end() {
loop.stop();
}
};
Response successfulSpriteImageResponse(const Resource& resource) {
EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url);
Response response;
response.data = std::make_unique<std::string>(util::read_file(resource.url));
return response;
}
Response successfulSpriteJSONResponse(const Resource& resource) {
EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url);
Response response;
response.data = std::make_unique<std::string>(util::read_file(resource.url));
return response;
}
Response failedSpriteResponse(const Resource&) {
Response response;
response.error = std::make_unique<Response::Error>(
Response::Error::Reason::Other,
"Failed by the test case");
return response;
}
Response corruptSpriteResponse(const Resource&) {
Response response;
response.data = std::make_unique<std::string>("CORRUPT");
return response;
}
TEST(SpriteAtlas, LoadingSuccess) {
SpriteAtlasTest test;
test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
FAIL() << util::toString(error);
test.end();
};
test.observer.spriteLoaded = [&] () {
EXPECT_EQ(1.0, test.spriteAtlas.getPixelRatio());
EXPECT_TRUE(test.spriteAtlas.isLoaded());
test.end();
};
test.run();
}
TEST(SpriteAtlas, JSONLoadingFail) {
SpriteAtlasTest test;
test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
test.fileSource.spriteJSONResponse = failedSpriteResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
EXPECT_EQ("Failed by the test case", util::toString(error));
EXPECT_FALSE(test.spriteAtlas.isLoaded());
test.end();
};
test.run();
}
TEST(SpriteAtlas, ImageLoadingFail) {
SpriteAtlasTest test;
test.fileSource.spriteImageResponse = failedSpriteResponse;
test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
EXPECT_EQ("Failed by the test case", util::toString(error));
EXPECT_FALSE(test.spriteAtlas.isLoaded());
test.end();
};
test.run();
}
TEST(SpriteAtlas, JSONLoadingCorrupted) {
SpriteAtlasTest test;
test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
test.fileSource.spriteJSONResponse = corruptSpriteResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error));
EXPECT_FALSE(test.spriteAtlas.isLoaded());
test.end();
};
test.run();
}
TEST(SpriteAtlas, ImageLoadingCorrupted) {
SpriteAtlasTest test;
test.fileSource.spriteImageResponse = corruptSpriteResponse;
test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
// Not asserting on platform-specific error text.
EXPECT_FALSE(test.spriteAtlas.isLoaded());
test.end();
};
test.run();
}
TEST(SpriteAtlas, LoadingCancel) {
SpriteAtlasTest test;
test.fileSource.spriteImageResponse =
test.fileSource.spriteJSONResponse = [&] (const Resource&) {
test.end();
return optional<Response>();
};
test.observer.spriteLoaded = [&] () {
FAIL() << "Should never be called";
};
test.run();
}

View File

@@ -0,0 +1,45 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/sprite/sprite_image_collection.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/string.hpp>
#include <utility>
using namespace mbgl;
TEST(SpriteImageCollection, OtherPixelRatio) {
FixtureLog log;
Images images;
// Adding mismatched sprite image
addSpriteImage(images, "one", std::make_unique<style::Image>(PremultipliedImage({ 8, 8 }), 2));
}
TEST(SpriteImageCollection, Replace) {
FixtureLog log;
Images images;
addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
auto image = images.find("sprite")->second.get();
addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
EXPECT_NE(image, images.find("sprite")->second.get());
}
TEST(SpriteImageCollection, ReplaceWithDifferentDimensions) {
FixtureLog log;
Images images;
addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 16, 16 }), 2));
addSpriteImage(images, "sprite", std::make_unique<style::Image>(PremultipliedImage({ 18, 18 }), 2));
EXPECT_EQ(1u, log.count({
EventSeverity::Warning,
Event::Sprite,
int64_t(-1),
"Can't change sprite dimensions for 'sprite'",
}));
}

View File

@@ -0,0 +1,164 @@
#include <mbgl/test/util.hpp>
#include <mbgl/test/fixture_log_observer.hpp>
#include <mbgl/test/stub_file_source.hpp>
#include <mbgl/test/stub_style_observer.hpp>
#include <mbgl/sprite/sprite_loader.hpp>
#include <mbgl/sprite/sprite_parser.hpp>
#include <mbgl/util/io.hpp>
#include <mbgl/util/image.hpp>
#include <mbgl/util/run_loop.hpp>
#include <mbgl/util/default_thread_pool.hpp>
#include <mbgl/util/string.hpp>
#include <utility>
using namespace mbgl;
class SpriteLoaderTest {
public:
SpriteLoaderTest() = default;
util::RunLoop loop;
StubFileSource fileSource;
StubStyleObserver observer;
ThreadPool threadPool { 1 };
SpriteLoader spriteLoader{ 1 };
void run() {
// Squelch logging.
Log::setObserver(std::make_unique<Log::NullObserver>());
spriteLoader.setObserver(&observer);
spriteLoader.load("test/fixtures/resources/sprite", threadPool, fileSource);
loop.run();
}
void end() {
loop.stop();
}
};
Response successfulSpriteImageResponse(const Resource& resource) {
EXPECT_EQ("test/fixtures/resources/sprite.png", resource.url);
Response response;
response.data = std::make_unique<std::string>(util::read_file(resource.url));
return response;
}
Response successfulSpriteJSONResponse(const Resource& resource) {
EXPECT_EQ("test/fixtures/resources/sprite.json", resource.url);
Response response;
response.data = std::make_unique<std::string>(util::read_file(resource.url));
return response;
}
Response failedSpriteResponse(const Resource&) {
Response response;
response.error = std::make_unique<Response::Error>(
Response::Error::Reason::Other,
"Failed by the test case");
return response;
}
Response corruptSpriteResponse(const Resource&) {
Response response;
response.data = std::make_unique<std::string>("CORRUPT");
return response;
}
TEST(SpriteLoader, LoadingSuccess) {
SpriteLoaderTest test;
test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
FAIL() << util::toString(error);
test.end();
};
test.observer.spriteLoaded = [&] (SpriteLoaderObserver::Images&& images) {
EXPECT_EQ(images.size(), Images::size_type(367));
test.end();
};
test.run();
}
TEST(SpriteLoader, JSONLoadingFail) {
SpriteLoaderTest test;
test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
test.fileSource.spriteJSONResponse = failedSpriteResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
EXPECT_EQ("Failed by the test case", util::toString(error));
test.end();
};
test.run();
}
TEST(SpriteLoader, ImageLoadingFail) {
SpriteLoaderTest test;
test.fileSource.spriteImageResponse = failedSpriteResponse;
test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
EXPECT_EQ("Failed by the test case", util::toString(error));
test.end();
};
test.run();
}
TEST(SpriteLoader, JSONLoadingCorrupted) {
SpriteLoaderTest test;
test.fileSource.spriteImageResponse = successfulSpriteImageResponse;
test.fileSource.spriteJSONResponse = corruptSpriteResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
EXPECT_EQ("Failed to parse JSON: Invalid value. at offset 0", util::toString(error));
test.end();
};
test.run();
}
TEST(SpriteLoader, ImageLoadingCorrupted) {
SpriteLoaderTest test;
test.fileSource.spriteImageResponse = corruptSpriteResponse;
test.fileSource.spriteJSONResponse = successfulSpriteJSONResponse;
test.observer.spriteError = [&] (std::exception_ptr error) {
EXPECT_TRUE(error != nullptr);
// Not asserting on platform-specific error text.
test.end();
};
test.run();
}
TEST(SpriteLoader, LoadingCancel) {
SpriteLoaderTest test;
test.fileSource.spriteImageResponse =
test.fileSource.spriteJSONResponse = [&] (const Resource&) {
test.end();
return optional<Response>();
};
test.observer.spriteLoaded = [&] (const SpriteLoaderObserver::Images&) {
FAIL() << "Should never be called";
};
test.run();
}

View File

@@ -145,11 +145,11 @@ TEST(Sprite, SpriteImageCreation1x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
EXPECT_EQ(18u, sprite->image.size.width);
EXPECT_EQ(18u, sprite->image.size.height);
EXPECT_EQ(1, sprite->pixelRatio);
EXPECT_EQ(18u, sprite->getImage().size.width);
EXPECT_EQ(18u, sprite->getImage().size.height);
EXPECT_EQ(1, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1x-museum.png"),
sprite->image);
sprite->getImage());
}
}
@@ -161,11 +161,11 @@ TEST(Sprite, SpriteImageCreation2x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
EXPECT_EQ(36u, sprite->image.size.width);
EXPECT_EQ(36u, sprite->image.size.height);
EXPECT_EQ(2, sprite->pixelRatio);
EXPECT_EQ(36u, sprite->getImage().size.width);
EXPECT_EQ(36u, sprite->getImage().size.height);
EXPECT_EQ(2, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation2x.png"),
sprite->image);
sprite->getImage());
}
TEST(Sprite, SpriteImageCreation1_5x) {
@@ -176,22 +176,22 @@ TEST(Sprite, SpriteImageCreation1_5x) {
ASSERT_TRUE(sprite.get());
EXPECT_EQ(24, sprite->getWidth());
EXPECT_EQ(24, sprite->getHeight());
EXPECT_EQ(36u, sprite->image.size.width);
EXPECT_EQ(36u, sprite->image.size.height);
EXPECT_EQ(1.5, sprite->pixelRatio);
EXPECT_EQ(36u, sprite->getImage().size.width);
EXPECT_EQ(36u, sprite->getImage().size.height);
EXPECT_EQ(1.5, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-museum.png"),
sprite->image);
sprite->getImage());
// "hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false}
const auto sprite2 = createStyleImage(image_2x, 314, 518, 35, 35, 1.5, false);
ASSERT_TRUE(sprite2.get());
EXPECT_EQ(float(35 / 1.5), sprite2->getWidth());
EXPECT_EQ(float(35 / 1.5), sprite2->getHeight());
EXPECT_EQ(35u, sprite2->image.size.width);
EXPECT_EQ(35u, sprite2->image.size.height);
EXPECT_EQ(1.5, sprite2->pixelRatio);
EXPECT_EQ(35u, sprite2->getImage().size.width);
EXPECT_EQ(35u, sprite2->getImage().size.height);
EXPECT_EQ(1.5, sprite2->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteimagecreation1_5x-hospital.png"),
sprite2->image);
sprite2->getImage());
}
TEST(Sprite, SpriteParsing) {
@@ -283,10 +283,10 @@ TEST(Sprite, SpriteParsing) {
auto& sprite = images.find("generic-metro")->second;
EXPECT_EQ(18, sprite->getWidth());
EXPECT_EQ(18, sprite->getHeight());
EXPECT_EQ(18u, sprite->image.size.width);
EXPECT_EQ(18u, sprite->image.size.height);
EXPECT_EQ(1, sprite->pixelRatio);
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->image);
EXPECT_EQ(18u, sprite->getImage().size.width);
EXPECT_EQ(18u, sprite->getImage().size.height);
EXPECT_EQ(1, sprite->getPixelRatio());
EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteparsing.png"), sprite->getImage());
}
}

View File

@@ -18,8 +18,8 @@ public:
if (glyphsError) glyphsError(fontStack, glyphRange, error);
}
void onSpriteLoaded() override {
if (spriteLoaded) spriteLoaded();
void onSpriteLoaded(Images&& images) override {
if (spriteLoaded) spriteLoaded(std::move(images));
}
void onSpriteError(std::exception_ptr error) override {
@@ -48,7 +48,7 @@ public:
std::function<void (const FontStack&, const GlyphRange&)> glyphsLoaded;
std::function<void (const FontStack&, const GlyphRange&, std::exception_ptr)> glyphsError;
std::function<void ()> spriteLoaded;
std::function<void (Images&&)> spriteLoaded;
std::function<void (std::exception_ptr)> spriteError;
std::function<void (Source&)> sourceLoaded;
std::function<void (Source&)> sourceChanged;

View File

@@ -36,17 +36,17 @@ TEST(StyleImage, ZeroRatio) {
TEST(StyleImage, Retina) {
style::Image image(PremultipliedImage({ 32, 24 }), 2.0);
EXPECT_EQ(16, image.getWidth());
EXPECT_EQ(32u, image.image.size.width);
EXPECT_EQ(32u, image.getImage().size.width);
EXPECT_EQ(12, image.getHeight());
EXPECT_EQ(24u, image.image.size.height);
EXPECT_EQ(2, image.pixelRatio);
EXPECT_EQ(24u, image.getImage().size.height);
EXPECT_EQ(2, image.getPixelRatio());
}
TEST(StyleImage, FractionalRatio) {
style::Image image(PremultipliedImage({ 20, 12 }), 1.5);
EXPECT_EQ(float(20.0 / 1.5), image.getWidth());
EXPECT_EQ(20u, image.image.size.width);
EXPECT_EQ(20u, image.getImage().size.width);
EXPECT_EQ(float(12.0 / 1.5), image.getHeight());
EXPECT_EQ(12u, image.image.size.height);
EXPECT_EQ(1.5, image.pixelRatio);
EXPECT_EQ(12u, image.getImage().size.height);
EXPECT_EQ(1.5, image.getPixelRatio());
}