diff options
-rw-r--r-- | src/mbgl/annotation/annotation_manager.cpp | 2 | ||||
-rw-r--r-- | src/mbgl/gl/context.cpp | 12 | ||||
-rw-r--r-- | src/mbgl/gl/context.hpp | 7 | ||||
-rw-r--r-- | src/mbgl/renderer/painter_symbol.cpp | 4 | ||||
-rw-r--r-- | src/mbgl/shader/symbol_uniforms.cpp | 12 | ||||
-rw-r--r-- | src/mbgl/shader/symbol_uniforms.hpp | 7 | ||||
-rw-r--r-- | src/mbgl/sprite/sprite_atlas.cpp | 126 | ||||
-rw-r--r-- | src/mbgl/sprite/sprite_atlas.hpp | 33 | ||||
-rw-r--r-- | src/mbgl/style/style.cpp | 2 | ||||
-rw-r--r-- | test/sprite/sprite_atlas.test.cpp | 68 |
10 files changed, 110 insertions, 163 deletions
diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index c7c13d0d2c..f8c1c3adf7 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -20,7 +20,7 @@ const std::string AnnotationManager::SourceID = "com.mapbox.annotations"; const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; AnnotationManager::AnnotationManager(float pixelRatio) - : spriteAtlas(1024, 1024, pixelRatio) { + : spriteAtlas({ 1024, 1024 }, pixelRatio) { struct NullFileSource : public FileSource { std::unique_ptr<AsyncRequest> request(const Resource&, Callback) override { diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index acecf2b698..7200b75050 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -185,15 +185,21 @@ Framebuffer Context::createFramebuffer(const Texture& color) { UniqueTexture Context::createTexture(const Size size, const void* data, TextureUnit unit) { auto obj = createTexture(); - activeTexture = unit; - texture[unit] = obj; + updateTexture(obj, size, data, unit); + // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures. + // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus. MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + return obj; +} + +void Context::updateTexture(TextureID id, const Size size, const void* data, TextureUnit unit) { + activeTexture = unit; + texture[unit] = id; MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data)); - return obj; } void Context::bindTexture(Texture& obj, diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 84c83f8d31..ae2b71994f 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -68,6 +68,12 @@ public: return { image.size, createTexture(image.size, image.data.get(), unit) }; } + template <typename Image> + void updateTexture(Texture& obj, const Image& image, TextureUnit unit = 0) { + updateTexture(obj.texture.get(), image.size, image.data.get(), unit); + obj.size = image.size; + } + // Creates an empty texture with the specified dimensions. Texture createTexture(const Size size, TextureUnit unit = 0) { return { size, createTexture(size, nullptr, unit) }; @@ -148,6 +154,7 @@ private: UniqueBuffer createVertexBuffer(const void* data, std::size_t size); UniqueBuffer createIndexBuffer(const void* data, std::size_t size); UniqueTexture createTexture(Size size, const void* data, TextureUnit); + void updateTexture(TextureID, Size size, const void* data, TextureUnit); UniqueFramebuffer createFramebuffer(); UniqueRenderbuffer createRenderbuffer(RenderbufferType, Size size); diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index d4ac5f94e9..a00389ce20 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -72,7 +72,7 @@ void Painter::renderSymbol(PaintParameters& parameters, const bool iconTransformed = values.rotationAlignment == AlignmentType::Map || state.getPitch() != 0; atlas.bind(bucket.sdfIcons || state.isChanging() || iconScaled || iconTransformed, context, 0); - std::array<uint16_t, 2> texsize {{ atlas.getWidth(), atlas.getHeight() }}; + const Size texsize = atlas.getSize(); if (bucket.sdfIcons) { if (values.hasHalo()) { @@ -101,7 +101,7 @@ void Painter::renderSymbol(PaintParameters& parameters, auto values = layer.impl->textPropertyValues(layout); - std::array<uint16_t, 2> texsize {{ glyphAtlas->width, glyphAtlas->height }}; + const Size texsize { glyphAtlas->width, glyphAtlas->height }; if (values.hasHalo()) { draw(parameters.shaders.symbolGlyph, diff --git a/src/mbgl/shader/symbol_uniforms.cpp b/src/mbgl/shader/symbol_uniforms.cpp index cd9f33de71..7071eff07c 100644 --- a/src/mbgl/shader/symbol_uniforms.cpp +++ b/src/mbgl/shader/symbol_uniforms.cpp @@ -9,7 +9,7 @@ using namespace style; template <class Values, class...Args> Values makeValues(const style::SymbolPropertyValues& values, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, @@ -35,7 +35,7 @@ Values makeValues(const style::SymbolPropertyValues& values, state), values.opacity, extrudeScale, - std::array<float, 2> {{ texsize[0] / 4.0f, texsize[1] / 4.0f }}, + std::array<float, 2> {{ float(texsize.width) / 4, float(texsize.height) / 4 }}, (state.getZoom() - zoomAdjust) * 10.0f, values.rotationAlignment == AlignmentType::Map, 0, @@ -46,7 +46,7 @@ Values makeValues(const style::SymbolPropertyValues& values, SymbolIconUniforms::Values SymbolIconUniforms::values(const style::SymbolPropertyValues& values, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state) @@ -61,7 +61,7 @@ SymbolIconUniforms::values(const style::SymbolPropertyValues& values, } static SymbolSDFUniforms::Values makeSDFValues(const style::SymbolPropertyValues& values, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, @@ -95,7 +95,7 @@ static SymbolSDFUniforms::Values makeSDFValues(const style::SymbolPropertyValues SymbolSDFUniforms::Values SymbolSDFUniforms::haloValues(const style::SymbolPropertyValues& values, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, @@ -121,7 +121,7 @@ SymbolSDFUniforms::haloValues(const style::SymbolPropertyValues& values, SymbolSDFUniforms::Values SymbolSDFUniforms::foregroundValues(const style::SymbolPropertyValues& values, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, diff --git a/src/mbgl/shader/symbol_uniforms.hpp b/src/mbgl/shader/symbol_uniforms.hpp index dc1b25004f..a38837c051 100644 --- a/src/mbgl/shader/symbol_uniforms.hpp +++ b/src/mbgl/shader/symbol_uniforms.hpp @@ -1,6 +1,7 @@ #pragma once #include <mbgl/shader/uniforms.hpp> +#include <mbgl/util/size.hpp> #include <array> @@ -35,7 +36,7 @@ struct SymbolIconUniforms : gl::Uniforms< uniforms::u_fadetexture> { static Values values(const style::SymbolPropertyValues&, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile&, const TransformState&); @@ -59,14 +60,14 @@ struct SymbolSDFUniforms : gl::Uniforms< uniforms::u_pitch_with_map> { static Values haloValues(const style::SymbolPropertyValues&, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile&, const TransformState&, float pixelRatio); static Values foregroundValues(const style::SymbolPropertyValues&, - const std::array<uint16_t, 2>& texsize, + const Size& texsize, const std::array<float, 2>& pixelsToGLUnits, const RenderTile&, const TransformState&, diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp index f8417c3372..24563ff43d 100644 --- a/src/mbgl/sprite/sprite_atlas.cpp +++ b/src/mbgl/sprite/sprite_atlas.cpp @@ -1,7 +1,6 @@ #include <mbgl/sprite/sprite_atlas.hpp> #include <mbgl/sprite/sprite_atlas_observer.hpp> #include <mbgl/sprite/sprite_parser.hpp> -#include <mbgl/gl/gl.hpp> #include <mbgl/gl/context.hpp> #include <mbgl/platform/log.hpp> #include <mbgl/platform/platform.hpp> @@ -28,15 +27,12 @@ struct SpriteAtlas::Loader { std::unique_ptr<AsyncRequest> spriteRequest; }; -SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_) - : width(width_), - height(height_), - pixelWidth(std::ceil(width * pixelRatio_)), - pixelHeight(std::ceil(height * pixelRatio_)), +SpriteAtlas::SpriteAtlas(Size size_, float pixelRatio_) + : size(std::move(size_)), pixelRatio(pixelRatio_), observer(&nullObserver), - bin(width_, height_), - dirtyFlag(true) { + bin(size.width, size.height), + dirty(true) { } SpriteAtlas::~SpriteAtlas() = default; @@ -162,7 +158,7 @@ std::shared_ptr<const SpriteImage> SpriteAtlas::getSprite(const std::string& nam } } -Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const SpriteImage& spriteImage) { +Rect<uint16_t> SpriteAtlas::allocateImage(const SpriteImage& spriteImage) { const uint16_t pixel_width = std::ceil(spriteImage.image.size.width / pixelRatio); const uint16_t pixel_height = std::ceil(spriteImage.image.size.height / pixelRatio); @@ -175,7 +171,7 @@ Rect<SpriteAtlas::dimension> SpriteAtlas::allocateImage(const SpriteImage& sprit // We have to allocate a new area in the bin, and store an empty image in it. // Add a 1px border around every image. - Rect<dimension> rect = bin.allocate(pack_width, pack_height); + Rect<uint16_t> rect = bin.allocate(pack_width, pack_height); if (rect.w == 0) { return rect; } @@ -197,7 +193,7 @@ optional<SpriteAtlasElement> SpriteAtlas::getImage(const std::string& name, return {}; } - Rect<dimension> rect = allocateImage(*sprite); + Rect<uint16_t> rect = allocateImage(*sprite); if (rect.w == 0) { if (debug::spriteWarnings) { Log::Warning(Event::Sprite, "sprite atlas bitmap overflow"); @@ -230,8 +226,8 @@ optional<SpriteAtlasPosition> SpriteAtlas::getPosition(const std::string& name, return SpriteAtlasPosition { {{ float(spriteImage->getWidth()), spriteImage->getHeight() }}, - {{ float(rect.x + padding) / width, float(rect.y + padding) / height }}, - {{ float(rect.x + padding + w) / width, float(rect.y + padding + h) / height }} + {{ float(rect.x + padding) / size.width, float(rect.y + padding) / size.height }}, + {{ float(rect.x + padding + w) / size.width, float(rect.y + padding + h) / size.height }} }; } @@ -264,29 +260,25 @@ void copyBitmap(const uint32_t *src, const uint32_t srcStride, const uint32_t sr } void SpriteAtlas::copy(const Holder& holder, const SpritePatternMode mode) { - if (!data) { - data = std::make_unique<uint32_t[]>(pixelWidth * pixelHeight); - std::fill(data.get(), data.get() + pixelWidth * pixelHeight, 0); + if (!image.valid()) { + image = PremultipliedImage({ static_cast<uint32_t>(std::ceil(size.width * pixelRatio)), + static_cast<uint32_t>(std::ceil(size.height * pixelRatio)) }); + std::fill(image.data.get(), image.data.get() + image.bytes(), 0); } - const uint32_t *srcData = reinterpret_cast<const uint32_t *>(holder.spriteImage->image.data.get()); + const uint32_t* srcData = + reinterpret_cast<const uint32_t*>(holder.spriteImage->image.data.get()); if (!srcData) return; - uint32_t *const dstData = data.get(); + uint32_t* const dstData = reinterpret_cast<uint32_t*>(image.data.get()); const int padding = 1; - copyBitmap(srcData, holder.spriteImage->image.size.width, 0, 0, dstData, pixelWidth, + copyBitmap(srcData, holder.spriteImage->image.size.width, 0, 0, dstData, image.size.width, (holder.pos.x + padding) * pixelRatio, (holder.pos.y + padding) * pixelRatio, - pixelWidth * pixelHeight, holder.spriteImage->image.size.width, + image.size.width * image.size.height, holder.spriteImage->image.size.width, holder.spriteImage->image.size.height, mode); - dirtyFlag = true; -} - -void SpriteAtlas::upload(gl::Context& context, gl::TextureUnit unit) { - if (dirtyFlag) { - bind(false, context, unit); - } + dirty = true; } void SpriteAtlas::updateDirty() { @@ -317,77 +309,31 @@ void SpriteAtlas::updateDirty() { dirtySprites.clear(); } -void SpriteAtlas::bind(bool linear, gl::Context& context, gl::TextureUnit unit) { - if (!data) { - return; // Empty atlas +void SpriteAtlas::upload(gl::Context& context, gl::TextureUnit unit) { + if (!texture) { + texture = context.createTexture(image, unit); + } else if (dirty) { + context.updateTexture(*texture, image, unit); } - if (!texture) { - texture = context.createTexture(); - context.activeTexture = unit; - context.texture[unit] = *texture; #if not MBGL_USE_GLES2 - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0)); +// if (dirty) { +// platform::showColorDebugImage("Sprite Atlas", +// reinterpret_cast<const char*>(image.data.get()), size.width, +// size.height, image.size.width, image.size.height); +// } #endif // MBGL_USE_GLES2 - // We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT on NPOT textures. - // We use those when the pixelRatio isn't a power of two, e.g. on iPhone 6 Plus. - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); - fullUploadRequired = true; - } else if (context.texture[unit] != *texture) { - context.activeTexture = unit; - context.texture[unit] = *texture; - } - GLuint filter_val = linear ? GL_LINEAR : GL_NEAREST; - if (filter_val != filter) { - context.activeTexture = unit; - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter_val)); - MBGL_CHECK_ERROR(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter_val)); - filter = filter_val; - } - - if (dirtyFlag) { - std::lock_guard<std::recursive_mutex> lock(mtx); - - context.activeTexture = unit; - if (fullUploadRequired) { - MBGL_CHECK_ERROR(glTexImage2D( - GL_TEXTURE_2D, // GLenum target - 0, // GLint level - GL_RGBA, // GLint internalformat - pixelWidth, // GLsizei width - pixelHeight, // GLsizei height - 0, // GLint border - GL_RGBA, // GLenum format - GL_UNSIGNED_BYTE, // GLenum type - data.get() // const GLvoid * data - )); - fullUploadRequired = false; - } else { - MBGL_CHECK_ERROR(glTexSubImage2D( - GL_TEXTURE_2D, // GLenum target - 0, // GLint level - 0, // GLint xoffset - 0, // GLint yoffset - pixelWidth, // GLsizei width - pixelHeight, // GLsizei height - GL_RGBA, // GLenum format - GL_UNSIGNED_BYTE, // GLenum type - data.get() // const GLvoid *pixels - )); - } - - dirtyFlag = false; + dirty = false; +} -#if not MBGL_USE_GLES2 - // platform::showColorDebugImage("Sprite Atlas", reinterpret_cast<const char*>(data.get()), - // pixelWidth, pixelHeight, pixelWidth, pixelHeight); -#endif // MBGL_USE_GLES2 - } +void SpriteAtlas::bind(bool linear, gl::Context& context, gl::TextureUnit unit) { + upload(context, unit); + context.bindTexture(*texture, unit, + linear ? gl::TextureFilter::Linear : gl::TextureFilter::Nearest); } -SpriteAtlas::Holder::Holder(std::shared_ptr<const SpriteImage> spriteImage_, Rect<dimension> pos_) +SpriteAtlas::Holder::Holder(std::shared_ptr<const SpriteImage> spriteImage_, Rect<uint16_t> pos_) : spriteImage(std::move(spriteImage_)), pos(std::move(pos_)) { } diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp index 3e0ca51508..c79aec135e 100644 --- a/src/mbgl/sprite/sprite_atlas.hpp +++ b/src/mbgl/sprite/sprite_atlas.hpp @@ -1,7 +1,7 @@ #pragma once #include <mbgl/geometry/binpack.hpp> -#include <mbgl/gl/object.hpp> +#include <mbgl/gl/texture.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/optional.hpp> #include <mbgl/sprite/sprite_image.hpp> @@ -47,10 +47,9 @@ enum class SpritePatternMode : bool { class SpriteAtlas : public util::noncopyable { public: - typedef uint16_t dimension; using Sprites = std::map<std::string, std::shared_ptr<const SpriteImage>>; - SpriteAtlas(dimension width, dimension height, float pixelRatio); + SpriteAtlas(Size, float pixelRatio); ~SpriteAtlas(); void load(const std::string& url, FileSource&); @@ -93,21 +92,19 @@ public: // the texture is only bound when the data is out of date (=dirty). void upload(gl::Context&, gl::TextureUnit unit); - dimension getWidth() const { return width; } - dimension getHeight() const { return height; } - dimension getTextureWidth() const { return pixelWidth; } - dimension getTextureHeight() const { return pixelHeight; } + Size getSize() const { return size; } float getPixelRatio() const { return pixelRatio; } // Only for use in tests. - const uint32_t* getData() const { return data.get(); } + const PremultipliedImage& getAtlasImage() const { + return image; + } private: void _setSprite(const std::string&, const std::shared_ptr<const SpriteImage>& = nullptr); void emitSpriteLoadedIfComplete(); - const dimension width, height; - const dimension pixelWidth, pixelHeight; + const Size size; const float pixelRatio; struct Loader; @@ -127,26 +124,24 @@ private: Sprites dirtySprites; struct Holder : private util::noncopyable { - Holder(std::shared_ptr<const SpriteImage>, Rect<dimension>); + Holder(std::shared_ptr<const SpriteImage>, Rect<uint16_t>); Holder(Holder&&); std::shared_ptr<const SpriteImage> spriteImage; - const Rect<dimension> pos; + const Rect<uint16_t> pos; }; using Key = std::pair<std::string, SpritePatternMode>; - Rect<SpriteAtlas::dimension> allocateImage(const SpriteImage&); + Rect<uint16_t> allocateImage(const SpriteImage&); void copy(const Holder& holder, SpritePatternMode mode); std::recursive_mutex mtx; - BinPack<dimension> bin; + BinPack<uint16_t> bin; std::map<Key, Holder> images; std::unordered_set<std::string> uninitialized; - std::unique_ptr<uint32_t[]> data; - std::atomic<bool> dirtyFlag; - bool fullUploadRequired = true; - mbgl::optional<gl::UniqueTexture> texture; - uint32_t filter = 0; + PremultipliedImage image; + mbgl::optional<gl::Texture> texture; + std::atomic<bool> dirty; static const int buffer = 1; }; diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp index cec922f45e..c33a10517d 100644 --- a/src/mbgl/style/style.cpp +++ b/src/mbgl/style/style.cpp @@ -39,7 +39,7 @@ static Observer nullObserver; Style::Style(FileSource& fileSource_, float pixelRatio) : fileSource(fileSource_), glyphAtlas(std::make_unique<GlyphAtlas>(2048, 2048, fileSource)), - spriteAtlas(std::make_unique<SpriteAtlas>(1024, 1024, pixelRatio)), + spriteAtlas(std::make_unique<SpriteAtlas>(Size{ 1024, 1024 }, pixelRatio)), lineAtlas(std::make_unique<LineAtlas>(256, 512)), observer(&nullObserver) { glyphAtlas->setObserver(this); diff --git a/test/sprite/sprite_atlas.test.cpp b/test/sprite/sprite_atlas.test.cpp index 231e2b1246..c9576013d4 100644 --- a/test/sprite/sprite_atlas.test.cpp +++ b/test/sprite/sprite_atlas.test.cpp @@ -21,14 +21,6 @@ auto readImage(const std::string& name) { return decodeImage(util::read_file(name)); } -auto imageFromAtlas(const SpriteAtlas& atlas) { - const size_t bytes = atlas.getTextureWidth() * atlas.getTextureHeight() * 4; - auto data = std::make_unique<uint8_t[]>(bytes); - const auto src = reinterpret_cast<const uint8_t*>(atlas.getData()); - std::copy(src, src + bytes, data.get()); - return PremultipliedImage{ { atlas.getTextureWidth(), atlas.getTextureHeight() }, std::move(data) }; -} - } // namespace TEST(SpriteAtlas, Basic) { @@ -37,17 +29,12 @@ TEST(SpriteAtlas, Basic) { auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), util::read_file("test/fixtures/annotations/emerald.json")); - SpriteAtlas atlas(63, 112, 1); + SpriteAtlas atlas({ 63, 112 }, 1); atlas.setSprites(spriteParseResult.get<Sprites>()); EXPECT_EQ(1.0f, atlas.getPixelRatio()); - EXPECT_EQ(63, atlas.getWidth()); - EXPECT_EQ(112, atlas.getHeight()); - EXPECT_EQ(63, atlas.getTextureWidth()); - EXPECT_EQ(112, atlas.getTextureHeight()); - - // Image hasn't been created yet. - EXPECT_FALSE(atlas.getData()); + EXPECT_EQ(63u, atlas.getSize().width); + EXPECT_EQ(112u, atlas.getSize().height); auto metro = *atlas.getImage("metro", SpritePatternMode::Single); EXPECT_EQ(0, metro.pos.x); @@ -60,7 +47,8 @@ TEST(SpriteAtlas, Basic) { EXPECT_EQ(18u, metro.spriteImage->image.size.height); EXPECT_EQ(1.0f, metro.spriteImage->pixelRatio); - EXPECT_TRUE(atlas.getData()); + EXPECT_EQ(63u, atlas.getAtlasImage().size.width); + EXPECT_EQ(112u, atlas.getAtlasImage().size.height); auto pos = *atlas.getPosition("metro", SpritePatternMode::Single); EXPECT_DOUBLE_EQ(18, pos.size[0]); @@ -87,21 +75,19 @@ TEST(SpriteAtlas, Basic) { EXPECT_EQ(20, metro2.pos.w); EXPECT_EQ(20, metro2.pos.h); - EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas.png"), imageFromAtlas(atlas)); + EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas.png"), atlas.getAtlasImage()); } TEST(SpriteAtlas, Size) { auto spriteParseResult = parseSprite(util::read_file("test/fixtures/annotations/emerald.png"), util::read_file("test/fixtures/annotations/emerald.json")); - SpriteAtlas atlas(63, 112, 1.4); + SpriteAtlas atlas({ 63, 112 }, 1.4); atlas.setSprites(spriteParseResult.get<Sprites>()); EXPECT_DOUBLE_EQ(1.4f, atlas.getPixelRatio()); - EXPECT_EQ(63, atlas.getWidth()); - EXPECT_EQ(112, atlas.getHeight()); - EXPECT_EQ(89, atlas.getTextureWidth()); - EXPECT_EQ(157, atlas.getTextureHeight()); + EXPECT_EQ(63u, atlas.getSize().width); + EXPECT_EQ(112u, atlas.getSize().height); auto metro = *atlas.getImage("metro", SpritePatternMode::Single); EXPECT_EQ(0, metro.pos.x); @@ -114,18 +100,20 @@ TEST(SpriteAtlas, Size) { EXPECT_EQ(18u, metro.spriteImage->image.size.height); EXPECT_EQ(1.0f, metro.spriteImage->pixelRatio); + // Now the image was created lazily. + EXPECT_EQ(89u, atlas.getAtlasImage().size.width); + EXPECT_EQ(157u, atlas.getAtlasImage().size.height); + EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlassize.png"), - imageFromAtlas(atlas)); + atlas.getAtlasImage()); } TEST(SpriteAtlas, Updates) { - SpriteAtlas atlas(32, 32, 1); + SpriteAtlas atlas({ 32, 32 }, 1); EXPECT_EQ(1.0f, atlas.getPixelRatio()); - EXPECT_EQ(32, atlas.getWidth()); - EXPECT_EQ(32, atlas.getHeight()); - EXPECT_EQ(32, atlas.getTextureWidth()); - EXPECT_EQ(32, atlas.getTextureHeight()); + EXPECT_EQ(32u, atlas.getSize().width); + EXPECT_EQ(32u, atlas.getSize().height); atlas.setSprite("one", std::make_shared<SpriteImage>(PremultipliedImage({ 16, 12 }), 1)); auto one = *atlas.getImage("one", SpritePatternMode::Single); @@ -139,8 +127,12 @@ TEST(SpriteAtlas, Updates) { EXPECT_EQ(12u, one.spriteImage->image.size.height); EXPECT_EQ(1.0f, one.spriteImage->pixelRatio); + // Now the image was created lazily. + EXPECT_EQ(32u, atlas.getAtlasImage().size.width); + EXPECT_EQ(32u, atlas.getAtlasImage().size.height); + EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas-empty.png"), - imageFromAtlas(atlas)); + atlas.getAtlasImage()); // Update sprite PremultipliedImage image2({ 16, 12 }); @@ -153,13 +145,13 @@ TEST(SpriteAtlas, Updates) { // Atlas texture hasn't changed yet. EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas-empty.png"), - imageFromAtlas(atlas)); + atlas.getAtlasImage()); atlas.updateDirty(); // Now the atlas texture has changed. EXPECT_EQ(readImage("test/fixtures/annotations/result-spriteatlas-updated.png"), - imageFromAtlas(atlas)); + atlas.getAtlasImage()); } TEST(SpriteAtlas, AddRemove) { @@ -169,7 +161,7 @@ TEST(SpriteAtlas, AddRemove) { const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage({ 16, 16 }), 2); const auto sprite3 = std::make_shared<SpriteImage>(PremultipliedImage({ 16, 16 }), 2); - SpriteAtlas atlas(32, 32, 1); + SpriteAtlas atlas({ 32, 32 }, 1); // Adding single atlas.setSprite("one", sprite1); @@ -212,7 +204,7 @@ TEST(SpriteAtlas, OtherPixelRatio) { const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage({ 8, 8 }), 1); - SpriteAtlas atlas(32, 32, 1); + SpriteAtlas atlas({ 32, 32 }, 1); // Adding mismatched sprite image atlas.setSprite("one", sprite1); @@ -222,7 +214,7 @@ TEST(SpriteAtlas, Multiple) { const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage({ 16, 16 }), 2); const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage({ 16, 16 }), 2); - SpriteAtlas atlas(32, 32, 1); + SpriteAtlas atlas({ 32, 32 }, 1); atlas.setSprites({ { "one", sprite1 }, { "two", sprite2 }, @@ -235,7 +227,7 @@ TEST(SpriteAtlas, Replace) { const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage({ 16, 16 }), 2); const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage({ 16, 16 }), 2); - SpriteAtlas atlas(32, 32, 1); + SpriteAtlas atlas({ 32, 32 }, 1); atlas.setSprite("sprite", sprite1); EXPECT_EQ(sprite1, atlas.getSprite("sprite")); @@ -251,7 +243,7 @@ TEST(SpriteAtlas, ReplaceWithDifferentDimensions) { const auto sprite1 = std::make_shared<SpriteImage>(PremultipliedImage({ 16, 16 }), 2); const auto sprite2 = std::make_shared<SpriteImage>(PremultipliedImage({ 18, 18 }), 2); - SpriteAtlas atlas(32, 32, 1); + SpriteAtlas atlas({ 32, 32 }, 1); atlas.setSprite("sprite", sprite1); atlas.setSprite("sprite", sprite2); @@ -273,7 +265,7 @@ public: util::RunLoop loop; StubFileSource fileSource; StubStyleObserver observer; - SpriteAtlas spriteAtlas { 32, 32, 1 }; + SpriteAtlas spriteAtlas{ { 32, 32 }, 1 }; void run() { // Squelch logging. |