summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2016-10-26 16:39:25 -0700
committerKonstantin Käfer <mail@kkaefer.com>2016-11-01 18:41:52 +0100
commit9e3839781fdf1b1c6a2d61a5de9b2c7ddd68e9ed (patch)
treecde9eb4e99341e5200ca5a06a4a1d0fc602c9232
parente2981c890e9a758c4dee703fa9e25e5d9eff65ce (diff)
downloadqtlocation-mapboxgl-9e3839781fdf1b1c6a2d61a5de9b2c7ddd68e9ed.tar.gz
[core] convert SpriteAtlas to use managed texture handling
-rw-r--r--src/mbgl/annotation/annotation_manager.cpp2
-rw-r--r--src/mbgl/gl/context.cpp12
-rw-r--r--src/mbgl/gl/context.hpp7
-rw-r--r--src/mbgl/renderer/painter_symbol.cpp4
-rw-r--r--src/mbgl/shader/symbol_uniforms.cpp12
-rw-r--r--src/mbgl/shader/symbol_uniforms.hpp7
-rw-r--r--src/mbgl/sprite/sprite_atlas.cpp126
-rw-r--r--src/mbgl/sprite/sprite_atlas.hpp33
-rw-r--r--src/mbgl/style/style.cpp2
-rw-r--r--test/sprite/sprite_atlas.test.cpp68
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.