diff --git a/include/mbgl/sprite/sprite_image.hpp b/include/mbgl/sprite/sprite_image.hpp index c5b063ff7fd..4c375e58b9d 100644 --- a/include/mbgl/sprite/sprite_image.hpp +++ b/include/mbgl/sprite/sprite_image.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -12,27 +13,18 @@ namespace mbgl { class SpriteImage : private util::noncopyable { public: - SpriteImage( - uint16_t width, uint16_t height, float pixelRatio, std::string&& data, bool sdf = false); + SpriteImage(PremultipliedImage&&, float pixelRatio, bool sdf = false); - // Logical dimensions of the sprite image. - const uint16_t width; - const uint16_t height; + PremultipliedImage image; // Pixel ratio of the sprite image. const float pixelRatio; - // Physical dimensions of the sprite image. - const uint16_t pixelWidth; - const uint16_t pixelHeight; - - // A string of an RGBA8 representation of the sprite. It must have exactly - // (width * ratio) * (height * ratio) * 4 (RGBA) bytes. The scan lines may - // not have gaps between them (i.e. stride == 0). - const std::string data; - // Whether this image should be interpreted as a signed distance field icon. const bool sdf; + + float getWidth() const { return image.width / pixelRatio; } + float getHeight() const { return image.height / pixelRatio; } }; } // namespace mbgl diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index e8d68170a26..31fe119c007 100644 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #pragma clang diagnostic ignored "-Wunused-parameter" @@ -1227,14 +1228,19 @@ void JNICALL nativeAddAnnotationIcon(JNIEnv *env, jobject obj, jlong nativeMapVi jbyte* pixelData = env->GetByteArrayElements(jpixels, nullptr); jsize size = env->GetArrayLength(jpixels); - std::string pixels(reinterpret_cast(pixelData), size); env->ReleaseByteArrayElements(jpixels, pixelData, JNI_ABORT); + mbgl::PremultipliedImage premultipliedImage(width, height); + + if (premultipliedImage.size() != uint32_t(size)) { + throw mbgl::util::SpriteImageException("Sprite image pixel count mismatch"); + } + + std::copy(pixelData, pixelData + size, premultipliedImage.data.get()); + auto iconImage = std::make_shared( - uint16_t(width), - uint16_t(height), - float(scale), - std::move(pixels)); + std::move(premultipliedImage), + float(scale)); nativeMapView->getMap().addAnnotationIcon(symbolName, iconImage); } diff --git a/platform/default/glfw_view.cpp b/platform/default/glfw_view.cpp index 66a41b0e101..5e401f4b6e8 100644 --- a/platform/default/glfw_view.cpp +++ b/platform/default/glfw_view.cpp @@ -200,8 +200,8 @@ GLFWView::makeSpriteImage(int width, int height, float pixelRatio) { const int w = std::ceil(pixelRatio * width); const int h = std::ceil(pixelRatio * height); - std::string pixels(w * h * 4, '\x00'); - auto data = reinterpret_cast(const_cast(pixels.data())); + mbgl::PremultipliedImage image(w, h); + auto data = reinterpret_cast(image.data.get()); const int dist = (w / 2) * (w / 2); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { @@ -217,7 +217,7 @@ GLFWView::makeSpriteImage(int width, int height, float pixelRatio) { } } - return std::make_shared(width, height, pixelRatio, std::move(pixels)); + return std::make_shared(std::move(image), pixelRatio); } void GLFWView::nextOrientation() { diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 0bf67f8b4e8..367e6d145a7 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -2304,11 +2304,11 @@ - (void)installAnnotationImage:(MGLAnnotationImage *)annotationImage size_t width = CGImageGetWidth(image); size_t height = CGImageGetHeight(image); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - std::string pixels(width * height * 4, '\0'); + mbgl::PremultipliedImage cPremultipliedImage(width, height); size_t bytesPerPixel = 4; size_t bytesPerRow = bytesPerPixel * width; size_t bitsPerComponent = 8; - char *pixelData = const_cast(pixels.data()); + char *pixelData = reinterpret_cast(cPremultipliedImage.data.get()); CGContextRef context = CGBitmapContextCreate(pixelData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); CGContextDrawImage(context, CGRectMake(0, 0, width, height), image); CGContextRelease(context); @@ -2316,10 +2316,8 @@ - (void)installAnnotationImage:(MGLAnnotationImage *)annotationImage // add sprite auto cSpriteImage = std::make_shared( - uint16_t(width), - uint16_t(height), - float(annotationImage.image.scale), - std::move(pixels)); + std::move(cPremultipliedImage), + float(annotationImage.image.scale)); // sprite upload NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; diff --git a/platform/osx/src/MGLMapView.mm b/platform/osx/src/MGLMapView.mm index 620eb53b54e..08b3cbdc677 100644 --- a/platform/osx/src/MGLMapView.mm +++ b/platform/osx/src/MGLMapView.mm @@ -1531,10 +1531,11 @@ - (void)installAnnotationImage:(MGLAnnotationImage *)annotationImage { // Get the image’s raw pixel data as an RGBA buffer. std::string pixelString((const char *)rep.bitmapData, rep.pixelsWide * rep.pixelsHigh * 4 /* RGBA */); - auto cSpriteImage = std::make_shared((uint16_t)rep.pixelsWide, - (uint16_t)rep.pixelsHigh, - (float)(rep.pixelsWide / size.width), - std::move(pixelString)); + + mbgl::PremultipliedImage cPremultipliedImage(rep.pixelsWide, rep.pixelsHigh); + std::copy(rep.bitmapData, rep.bitmapData + cPremultipliedImage.size(), cPremultipliedImage.data.get()); + auto cSpriteImage = std::make_shared(std::move(cPremultipliedImage), + (float)(rep.pixelsWide / size.width)); NSString *symbolName = [MGLAnnotationSpritePrefix stringByAppendingString:annotationImage.reuseIdentifier]; _mbglMap->addAnnotationIcon(symbolName.UTF8String, cSpriteImage); diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index 4e395e06385..01b18847c54 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -165,7 +165,7 @@ void AnnotationManager::removeIcon(const std::string& name) { double AnnotationManager::getTopOffsetPixelsForIcon(const std::string& name) { auto sprite = spriteStore.getSprite(name); - return sprite ? -sprite->height / 2 : 0; + return sprite ? -(std::ceil(sprite->image.height / sprite->pixelRatio)) / 2 : 0; } } // namespace mbgl diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index a34cbcd0285..45e718fd941 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -248,8 +248,8 @@ void SymbolBucket::addFeatures(uintptr_t tileUID, auto image = spriteAtlas.getImage(feature.sprite, false); if (image) { shapedIcon = shapeIcon(*image, layout); - assert((*image).texture); - if ((*image).texture->sdf) { + assert((*image).spriteImage); + if ((*image).spriteImage->sdf) { sdfIcons = true; } if ((*image).relativePixelRatio != 1.0f) { diff --git a/src/mbgl/sprite/sprite_atlas.cpp b/src/mbgl/sprite/sprite_atlas.cpp index 497721a61c8..1a3b3688230 100644 --- a/src/mbgl/sprite/sprite_atlas.cpp +++ b/src/mbgl/sprite/sprite_atlas.cpp @@ -26,10 +26,10 @@ SpriteAtlas::SpriteAtlas(dimension width_, dimension height_, float pixelRatio_, dirty(true) { } -Rect SpriteAtlas::allocateImage(float src_width, float src_height) { +Rect SpriteAtlas::allocateImage(const SpriteImage& spriteImage) { - const uint16_t pixel_width = std::ceil(src_width / pixelRatio); - const uint16_t pixel_height = std::ceil(src_height / pixelRatio); + const uint16_t pixel_width = std::ceil(spriteImage.image.width / pixelRatio); + const uint16_t pixel_height = std::ceil(spriteImage.image.height / pixelRatio); // Increase to next number divisible by 4, but at least 1. // This is so we can scale down the texture coordinates and pack them @@ -52,7 +52,7 @@ mapbox::util::optional SpriteAtlas::getImage(const std::stri auto rect_it = images.find({ name, wrap }); if (rect_it != images.end()) { - return SpriteAtlasElement { rect_it->second.pos, rect_it->second.texture, rect_it->second.texture->pixelRatio / pixelRatio }; + return SpriteAtlasElement { rect_it->second.pos, rect_it->second.spriteImage, rect_it->second.spriteImage->pixelRatio / pixelRatio }; } auto sprite = store.getSprite(name); @@ -60,7 +60,7 @@ mapbox::util::optional SpriteAtlas::getImage(const std::stri return {}; } - Rect rect = allocateImage(sprite->width * sprite->pixelRatio, sprite->height * sprite->pixelRatio); + Rect rect = allocateImage(*sprite); if (rect.w == 0) { if (debug::spriteWarnings) { Log::Warning(Event::Sprite, "sprite atlas bitmap overflow"); @@ -85,13 +85,13 @@ mapbox::util::optional SpriteAtlas::getPosition(const std:: auto rect = (*img).pos; const float padding = 1; - auto image = (*img).texture; + auto spriteImage = (*img).spriteImage; - const float w = image->width * (*img).relativePixelRatio; - const float h = image->height * (*img).relativePixelRatio; + const float w = spriteImage->getWidth() * (*img).relativePixelRatio; + const float h = spriteImage->getHeight() * (*img).relativePixelRatio; return SpriteAtlasPosition { - {{ float(image->width), float(image->height) }}, + {{ 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 }} }; @@ -131,15 +131,15 @@ void SpriteAtlas::copy(const Holder& holder, const bool wrap) { std::fill(data.get(), data.get() + pixelWidth * pixelHeight, 0); } - const uint32_t *srcData = reinterpret_cast(holder.texture->data.data()); + const uint32_t *srcData = reinterpret_cast(holder.spriteImage->image.data.get()); if (!srcData) return; uint32_t *const dstData = data.get(); const int padding = 1; - copyBitmap(srcData, holder.texture->pixelWidth, 0, 0, + copyBitmap(srcData, uint32_t(holder.spriteImage->image.width), 0, 0, dstData, pixelWidth, (holder.pos.x + padding) * pixelRatio, (holder.pos.y + padding) * pixelRatio, pixelWidth * pixelHeight, - holder.texture->pixelWidth, holder.texture->pixelHeight, wrap); + uint32_t(holder.spriteImage->image.width), uint32_t(holder.spriteImage->image.height), wrap); dirty = true; } @@ -168,8 +168,8 @@ void SpriteAtlas::updateDirty() { } else { // The two names match; Holder& holder = imageIterator->second; - holder.texture = spriteIterator->second; - if (holder.texture != nullptr) { + holder.spriteImage = spriteIterator->second; + if (holder.spriteImage != nullptr) { copy(holder, imageIterator->first.second); ++imageIterator; } else { @@ -255,10 +255,10 @@ SpriteAtlas::~SpriteAtlas() { } } -SpriteAtlas::Holder::Holder(const std::shared_ptr& texture_, +SpriteAtlas::Holder::Holder(const std::shared_ptr& spriteImage_, const Rect& pos_) - : texture(texture_), pos(pos_) { + : spriteImage(spriteImage_), pos(pos_) { } -SpriteAtlas::Holder::Holder(Holder&& h) : texture(std::move(h.texture)), pos(h.pos) { +SpriteAtlas::Holder::Holder(Holder&& h) : spriteImage(std::move(h.spriteImage)), pos(h.pos) { } diff --git a/src/mbgl/sprite/sprite_atlas.hpp b/src/mbgl/sprite/sprite_atlas.hpp index aca82b3fea5..eb807dc933d 100644 --- a/src/mbgl/sprite/sprite_atlas.hpp +++ b/src/mbgl/sprite/sprite_atlas.hpp @@ -33,7 +33,7 @@ struct SpriteAtlasPosition { struct SpriteAtlasElement { const Rect pos; - const std::shared_ptr texture; + const std::shared_ptr spriteImage; const float relativePixelRatio; }; @@ -78,13 +78,13 @@ class SpriteAtlas : public util::noncopyable { struct Holder : private util::noncopyable { inline Holder(const std::shared_ptr&, const Rect&); inline Holder(Holder&&); - std::shared_ptr texture; + std::shared_ptr spriteImage; const Rect pos; }; using Key = std::pair; - Rect allocateImage(float width, float height); + Rect allocateImage(const SpriteImage&); void copy(const Holder& holder, const bool wrap); std::recursive_mutex mtx; diff --git a/src/mbgl/sprite/sprite_image.cpp b/src/mbgl/sprite/sprite_image.cpp index e55d676b34e..d7e422ed1d3 100644 --- a/src/mbgl/sprite/sprite_image.cpp +++ b/src/mbgl/sprite/sprite_image.cpp @@ -6,23 +6,17 @@ namespace mbgl { -SpriteImage::SpriteImage(const uint16_t pixelWidth_, - const uint16_t pixelHeight_, +SpriteImage::SpriteImage(PremultipliedImage&& image_, const float pixelRatio_, - std::string&& data_, bool sdf_) - : width(std::ceil(pixelWidth_ / pixelRatio_)), - height(std::ceil(pixelHeight_ / pixelRatio_)), + : image(std::move(image_)), pixelRatio(pixelRatio_), - pixelWidth(pixelWidth_), - pixelHeight(pixelHeight_), - data(std::move(data_)), sdf(sdf_) { - const size_t size = pixelWidth * pixelHeight * 4; - if (size == 0) { + + if (image.size() == 0) { throw util::SpriteImageException("Sprite image dimensions may not be zero"); - } else if (size != data.size()) { - throw util::SpriteImageException("Sprite image pixel count mismatch"); + } else if (pixelRatio <= 0) { + throw util::SpriteImageException("Sprite pixelRatio may not be <= 0"); } } diff --git a/src/mbgl/sprite/sprite_parser.cpp b/src/mbgl/sprite/sprite_parser.cpp index 6942d009f34..9baf9331775 100644 --- a/src/mbgl/sprite/sprite_parser.cpp +++ b/src/mbgl/sprite/sprite_parser.cpp @@ -26,10 +26,10 @@ SpriteImagePtr createSpriteImage(const PremultipliedImage& image, return nullptr; } - std::string data(width * height * 4, '\0'); + PremultipliedImage dstImage(width, height); auto srcData = reinterpret_cast(image.data.get()); - auto dstData = reinterpret_cast(const_cast(data.data())); + auto dstData = reinterpret_cast(dstImage.data.get()); const int32_t maxX = std::min(uint32_t(image.width), uint32_t(width + srcX)) - srcX; assert(maxX <= int32_t(image.width)); @@ -45,7 +45,7 @@ SpriteImagePtr createSpriteImage(const PremultipliedImage& image, } } - return std::make_unique(width, height, ratio, std::move(data), sdf); + return std::make_unique(std::move(dstImage), ratio, sdf); } namespace { diff --git a/src/mbgl/sprite/sprite_store.cpp b/src/mbgl/sprite/sprite_store.cpp index a8d6c2fcd38..e051b969a1a 100644 --- a/src/mbgl/sprite/sprite_store.cpp +++ b/src/mbgl/sprite/sprite_store.cpp @@ -113,7 +113,7 @@ void SpriteStore::_setSprite(const std::string& name, auto it = sprites.find(name); if (it != sprites.end()) { // There is already a sprite with that name in our store. - if ((it->second->width != sprite->width || it->second->height != sprite->height)) { + if ((it->second->image.width != sprite->image.width || it->second->image.height != sprite->image.height)) { Log::Warning(Event::Sprite, "Can't change sprite dimensions for '%s'", name.c_str()); return; } diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index f12658c669a..342a27a66f3 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -6,10 +6,10 @@ namespace mbgl { PositionedIcon shapeIcon(const SpriteAtlasElement& image, const SymbolLayoutProperties& layout) { float dx = layout.icon.offset.value[0]; float dy = layout.icon.offset.value[1]; - float x1 = dx - image.texture->width / 2.0f; - float x2 = x1 + image.texture->width; - float y1 = dy - image.texture->height / 2.0f; - float y2 = y1 + image.texture->height; + float x1 = dx - image.spriteImage->getWidth() / 2.0f; + float x2 = x1 + image.spriteImage->getWidth(); + float y1 = dy - image.spriteImage->getHeight() / 2.0f; + float y2 = y1 + image.spriteImage->getHeight(); return PositionedIcon(image, y1, y2, x1, x2); } diff --git a/test/api/annotations.cpp b/test/api/annotations.cpp index d80cff9e8ee..f427331eefd 100644 --- a/test/api/annotations.cpp +++ b/test/api/annotations.cpp @@ -16,7 +16,7 @@ using namespace mbgl; std::shared_ptr namedMarker(const std::string &name) { PremultipliedImage image = decodeImage(util::read_file("test/fixtures/sprites/" + name)); - return std::make_shared(image.width, image.height, 1.0, std::string(reinterpret_cast(image.data.get()), image.size())); + return std::make_shared(std::move(image), 1.0); } namespace { diff --git a/test/fixtures/util.cpp b/test/fixtures/util.cpp index 926e267631b..c2a5d83637c 100644 --- a/test/fixtures/util.cpp +++ b/test/fixtures/util.cpp @@ -89,6 +89,7 @@ Server::~Server() { } } + // from https://gist.github.com/ArtemGr/997887 uint64_t crc64(const char* data, size_t size) { boost::crc_optimal<64, 0x04C11DB7, 0, 0, false, false> crc; @@ -100,6 +101,10 @@ uint64_t crc64(const std::string& str) { return crc64(str.data(), str.size()); } +uint64_t crc64(const PremultipliedImage &image) { + return crc64(reinterpret_cast(image.data.get()), image.size()); +} + PremultipliedImage render(Map& map, std::chrono::milliseconds timeout) { std::promise promise; map.renderStill([&](std::exception_ptr, PremultipliedImage&& image) { diff --git a/test/fixtures/util.hpp b/test/fixtures/util.hpp index d7c9c63a890..7a240cb41f5 100644 --- a/test/fixtures/util.hpp +++ b/test/fixtures/util.hpp @@ -33,6 +33,7 @@ class Server { uint64_t crc64(const char*, size_t); uint64_t crc64(const std::string&); +uint64_t crc64(const PremultipliedImage&); PremultipliedImage render(Map&, std::chrono::milliseconds timeout = std::chrono::milliseconds(1000)); diff --git a/test/sprite/sprite_atlas.cpp b/test/sprite/sprite_atlas.cpp index 8e3c4317c08..4398168cd41 100644 --- a/test/sprite/sprite_atlas.cpp +++ b/test/sprite/sprite_atlas.cpp @@ -34,11 +34,11 @@ TEST(Sprite, SpriteAtlas) { EXPECT_EQ(0, metro.pos.y); EXPECT_EQ(20, metro.pos.w); EXPECT_EQ(20, metro.pos.h); - EXPECT_EQ(18, metro.texture->width); - EXPECT_EQ(18, metro.texture->height); - EXPECT_EQ(18, metro.texture->pixelWidth); - EXPECT_EQ(18, metro.texture->pixelHeight); - EXPECT_EQ(1.0f, metro.texture->pixelRatio); + EXPECT_EQ(18, metro.spriteImage->getWidth()); + EXPECT_EQ(18, metro.spriteImage->getHeight()); + EXPECT_EQ(18, metro.spriteImage->image.width); + EXPECT_EQ(18, metro.spriteImage->image.height); + EXPECT_EQ(1.0f, metro.spriteImage->pixelRatio); EXPECT_TRUE(atlas.getData()); @@ -96,11 +96,11 @@ TEST(Sprite, SpriteAtlasSize) { EXPECT_EQ(0, metro.pos.y); EXPECT_EQ(16, metro.pos.w); EXPECT_EQ(16, metro.pos.h); - EXPECT_EQ(18, metro.texture->width); - EXPECT_EQ(18, metro.texture->height); - EXPECT_EQ(18, metro.texture->pixelWidth); - EXPECT_EQ(18, metro.texture->pixelHeight); - EXPECT_EQ(1.0f, metro.texture->pixelRatio); + EXPECT_EQ(18, metro.spriteImage->getWidth()); + EXPECT_EQ(18, metro.spriteImage->getHeight()); + EXPECT_EQ(18, metro.spriteImage->image.width); + EXPECT_EQ(18, metro.spriteImage->image.height); + EXPECT_EQ(1.0f, metro.spriteImage->pixelRatio); const size_t bytes = atlas.getTextureWidth() * atlas.getTextureHeight() * 4; const auto hash = test::crc64(reinterpret_cast(atlas.getData()), bytes); @@ -122,24 +122,29 @@ TEST(Sprite, SpriteAtlasUpdates) { EXPECT_EQ(32, atlas.getTextureWidth()); EXPECT_EQ(32, atlas.getTextureHeight()); - store.setSprite("one", std::make_shared(16, 12, 1, std::string(16 * 12 * 4, '\x00'))); + PremultipliedImage image(16, 12); + store.setSprite("one", std::make_shared(std::move(image), 1)); auto one = *atlas.getImage("one", false); EXPECT_EQ(0, one.pos.x); EXPECT_EQ(0, one.pos.y); EXPECT_EQ(20, one.pos.w); EXPECT_EQ(16, one.pos.h); - EXPECT_EQ(16, one.texture->width); - EXPECT_EQ(12, one.texture->height); - EXPECT_EQ(16, one.texture->pixelWidth); - EXPECT_EQ(12, one.texture->pixelHeight); - EXPECT_EQ(1.0f, one.texture->pixelRatio); + EXPECT_EQ(16, one.spriteImage->getWidth()); + EXPECT_EQ(12, one.spriteImage->getHeight()); + EXPECT_EQ(16, one.spriteImage->image.width); + EXPECT_EQ(12, one.spriteImage->image.height); + EXPECT_EQ(1.0f, one.spriteImage->pixelRatio); const size_t bytes = atlas.getTextureWidth() * atlas.getTextureHeight() * 4; const auto hash = test::crc64(reinterpret_cast(atlas.getData()), bytes); EXPECT_EQ(0x0000000000000000u, hash) << std::hex << hash; // Update sprite - auto newSprite = std::make_shared(16, 12, 1, std::string(16 * 12 * 4, '\xFF')); + PremultipliedImage image2(16, 12); + for (size_t i = 0; i < image2.size(); i++) { + image2.data.get()[i] = 255; + } + auto newSprite = std::make_shared(std::move(image2), 1); store.setSprite("one", newSprite); ASSERT_EQ(newSprite, store.getSprite("one")); diff --git a/test/sprite/sprite_image.cpp b/test/sprite/sprite_image.cpp index 1ed4e56ff6c..c2e889134f0 100644 --- a/test/sprite/sprite_image.cpp +++ b/test/sprite/sprite_image.cpp @@ -1,13 +1,15 @@ #include "../fixtures/util.hpp" #include +#include #include using namespace mbgl; TEST(Sprite, SpriteImageZeroWidth) { + PremultipliedImage image(0, 16); try { - SpriteImage(0, 16, 2, ""); + SpriteImage(std::move(image), 2.0); FAIL() << "Expected exception"; } catch (util::SpriteImageException& ex) { EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); @@ -15,8 +17,9 @@ TEST(Sprite, SpriteImageZeroWidth) { } TEST(Sprite, SpriteImageZeroHeight) { + PremultipliedImage image(16, 0); try { - SpriteImage(16, 0, 2, ""); + SpriteImage(std::move(image), 2.0); FAIL() << "Expected exception"; } catch (util::SpriteImageException& ex) { EXPECT_STREQ("Sprite image dimensions may not be zero", ex.what()); @@ -24,41 +27,31 @@ TEST(Sprite, SpriteImageZeroHeight) { } TEST(Sprite, SpriteImageZeroRatio) { + PremultipliedImage image(16, 16); try { - SpriteImage(16, 16, 0, ""); + SpriteImage(std::move(image), 0.0); FAIL() << "Expected exception"; } catch (util::SpriteImageException& ex) { - EXPECT_STREQ("Sprite image pixel count mismatch", ex.what()); - } -} - -TEST(Sprite, SpriteImageMismatchedData) { - try { - SpriteImage(16, 16, 2, ""); - FAIL() << "Expected exception"; - } catch (util::SpriteImageException& ex) { - EXPECT_STREQ("Sprite image pixel count mismatch", ex.what()); + EXPECT_STREQ("Sprite pixelRatio may not be <= 0", ex.what()); } } TEST(Sprite, SpriteImage) { - std::string pixels(32 * 24 * 4, '\0'); - SpriteImage sprite(32, 24, 2, std::move(pixels)); - EXPECT_EQ(16, sprite.width); - EXPECT_EQ(32, sprite.pixelWidth); - EXPECT_EQ(12, sprite.height); - EXPECT_EQ(24, sprite.pixelHeight); + PremultipliedImage image(32, 24); + SpriteImage sprite(std::move(image), 2.0); + EXPECT_EQ(16, sprite.getWidth()); + EXPECT_EQ(32, sprite.image.width); + EXPECT_EQ(12, sprite.getHeight()); + EXPECT_EQ(24, sprite.image.height); EXPECT_EQ(2, sprite.pixelRatio); - EXPECT_EQ(32u * 24 * 4, sprite.data.size()); } TEST(Sprite, SpriteImageFractionalRatio) { - std::string pixels(20 * 12 * 4, '\0'); - SpriteImage sprite(20, 12, 1.5, std::move(pixels)); - EXPECT_EQ(14, sprite.width); - EXPECT_EQ(20, sprite.pixelWidth); - EXPECT_EQ(8, sprite.height); - EXPECT_EQ(12, sprite.pixelHeight); + PremultipliedImage image(20, 12); + SpriteImage sprite(std::move(image), 1.5); + EXPECT_EQ(float(20.0 / 1.5), sprite.getWidth()); + EXPECT_EQ(20, sprite.image.width); + EXPECT_EQ(float(12.0 / 1.5), sprite.getHeight()); + EXPECT_EQ(12, sprite.image.height); EXPECT_EQ(1.5, sprite.pixelRatio); - EXPECT_EQ(20u * 12 * 4, sprite.data.size()); } diff --git a/test/sprite/sprite_parser.cpp b/test/sprite/sprite_parser.cpp index 9eb19a8095a..54c013e9363 100644 --- a/test/sprite/sprite_parser.cpp +++ b/test/sprite/sprite_parser.cpp @@ -43,34 +43,34 @@ TEST(Sprite, SpriteImageCreation1x) { { // "museum_icon":{"x":177,"y":187,"width":18,"height":18,"pixelRatio":1,"sdf":false} const auto sprite = createSpriteImage(image_1x, 177, 187, 18, 18, 1, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(18, sprite->width); - EXPECT_EQ(18, sprite->height); - EXPECT_EQ(18, sprite->pixelWidth); - EXPECT_EQ(18, sprite->pixelHeight); + EXPECT_EQ(18, sprite->getWidth()); + EXPECT_EQ(18, sprite->getHeight()); + EXPECT_EQ(18, sprite->image.width); + EXPECT_EQ(18, sprite->image.height); EXPECT_EQ(1, sprite->pixelRatio); - EXPECT_EQ(0x7FCC5F263D1FFE16u, test::crc64(sprite->data)); + EXPECT_EQ(0x7FCC5F263D1FFE16u, test::crc64(sprite->image)); } { // outside image == blank const auto sprite = createSpriteImage(image_1x, 200, 0, 16, 16, 1, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(16, sprite->width); - EXPECT_EQ(16, sprite->height); - EXPECT_EQ(16, sprite->pixelWidth); - EXPECT_EQ(16, sprite->pixelHeight); + EXPECT_EQ(16, sprite->getWidth()); + EXPECT_EQ(16, sprite->getHeight()); + EXPECT_EQ(16, sprite->image.width); + EXPECT_EQ(16, sprite->image.height); EXPECT_EQ(1, sprite->pixelRatio); - EXPECT_EQ(0x0000000000000000u, test::crc64(sprite->data)) << std::hex << test::crc64(sprite->data); + EXPECT_EQ(0x0000000000000000u, test::crc64(sprite->image)) << std::hex << test::crc64(sprite->image); } { // outside image == blank const auto sprite = createSpriteImage(image_1x, 0, 300, 16, 16, 1, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(16, sprite->width); - EXPECT_EQ(16, sprite->height); - EXPECT_EQ(16, sprite->pixelWidth); - EXPECT_EQ(16, sprite->pixelHeight); + EXPECT_EQ(16, sprite->getWidth()); + EXPECT_EQ(16, sprite->getHeight()); + EXPECT_EQ(16, sprite->image.width); + EXPECT_EQ(16, sprite->image.height); EXPECT_EQ(1, sprite->pixelRatio); - EXPECT_EQ(0x0000000000000000u, test::crc64(sprite->data)) << std::hex << test::crc64(sprite->data); + EXPECT_EQ(0x0000000000000000u, test::crc64(sprite->image)) << std::hex << test::crc64(sprite->image); } } @@ -80,12 +80,12 @@ TEST(Sprite, SpriteImageCreation2x) { // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} const auto sprite = createSpriteImage(image_2x, 354, 374, 36, 36, 2, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(18, sprite->width); - EXPECT_EQ(18, sprite->height); - EXPECT_EQ(36, sprite->pixelWidth); - EXPECT_EQ(36, sprite->pixelHeight); + EXPECT_EQ(18, sprite->getWidth()); + EXPECT_EQ(18, sprite->getHeight()); + EXPECT_EQ(36, sprite->image.width); + EXPECT_EQ(36, sprite->image.height); EXPECT_EQ(2, sprite->pixelRatio); - EXPECT_EQ(0x85F345098DD4F9E3u, test::crc64(sprite->data)); + EXPECT_EQ(0x85F345098DD4F9E3u, test::crc64(sprite->image)); } TEST(Sprite, SpriteImageCreation1_5x) { @@ -94,22 +94,22 @@ TEST(Sprite, SpriteImageCreation1_5x) { // "museum_icon":{"x":354,"y":374,"width":36,"height":36,"pixelRatio":2,"sdf":false} const auto sprite = createSpriteImage(image_2x, 354, 374, 36, 36, 1.5, false); ASSERT_TRUE(sprite.get()); - EXPECT_EQ(24, sprite->width); - EXPECT_EQ(24, sprite->height); - EXPECT_EQ(36, sprite->pixelWidth); - EXPECT_EQ(36, sprite->pixelHeight); + EXPECT_EQ(24, sprite->getWidth()); + EXPECT_EQ(24, sprite->getHeight()); + EXPECT_EQ(36, sprite->image.width); + EXPECT_EQ(36, sprite->image.height); EXPECT_EQ(1.5, sprite->pixelRatio); - EXPECT_EQ(0x85F345098DD4F9E3u, test::crc64(sprite->data)); + EXPECT_EQ(0x85F345098DD4F9E3u, test::crc64(sprite->image)); // "hospital_icon":{"x":314,"y":518,"width":36,"height":36,"pixelRatio":2,"sdf":false} const auto sprite2 = createSpriteImage(image_2x, 314, 518, 35, 35, 1.5, false); ASSERT_TRUE(sprite2.get()); - EXPECT_EQ(24, sprite2->width); - EXPECT_EQ(24, sprite2->height); - EXPECT_EQ(35, sprite2->pixelWidth); - EXPECT_EQ(35, sprite2->pixelHeight); + EXPECT_EQ(float(35 / 1.5), sprite2->getWidth()); + EXPECT_EQ(float(35 / 1.5), sprite2->getHeight()); + EXPECT_EQ(35, sprite2->image.width); + EXPECT_EQ(35, sprite2->image.height); EXPECT_EQ(1.5, sprite2->pixelRatio); - EXPECT_EQ(14312995667113444493u, test::crc64(sprite2->data)); + EXPECT_EQ(14312995667113444493u, test::crc64(sprite2->image)); } TEST(Sprite, SpriteParsing) { @@ -199,12 +199,12 @@ TEST(Sprite, SpriteParsing) { { auto sprite = images.find("generic-metro")->second; - EXPECT_EQ(18, sprite->width); - EXPECT_EQ(18, sprite->height); - EXPECT_EQ(18, sprite->pixelWidth); - EXPECT_EQ(18, sprite->pixelHeight); + EXPECT_EQ(18, sprite->getWidth()); + EXPECT_EQ(18, sprite->getHeight()); + EXPECT_EQ(18, sprite->image.width); + EXPECT_EQ(18, sprite->image.height); EXPECT_EQ(1, sprite->pixelRatio); - EXPECT_EQ(0xFF56F5F48F707147u, test::crc64(sprite->data)); + EXPECT_EQ(0xFF56F5F48F707147u, test::crc64(sprite->image)); } } diff --git a/test/sprite/sprite_store.cpp b/test/sprite/sprite_store.cpp index 5056b89cb74..d055603dc2c 100644 --- a/test/sprite/sprite_store.cpp +++ b/test/sprite/sprite_store.cpp @@ -13,9 +13,10 @@ using namespace mbgl; TEST(SpriteStore, SpriteStore) { FixtureLog log; - const auto sprite1 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); - const auto sprite2 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); - const auto sprite3 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); + PremultipliedImage image(16, 16); + const auto sprite1 = std::make_shared(std::move(image), 2); + const auto sprite2 = std::make_shared(std::move(image), 2); + const auto sprite3 = std::make_shared(std::move(image), 2); using Sprites = std::map>; SpriteStore store(1); @@ -79,7 +80,8 @@ TEST(SpriteStore, SpriteStore) { TEST(SpriteStore, OtherPixelRatio) { FixtureLog log; - const auto sprite1 = std::make_shared(8, 8, 1, std::string(8 * 8 * 4, '\0')); + PremultipliedImage image(8, 8); + const auto sprite1 = std::make_shared(std::move(image), 1); using Sprites = std::map>; SpriteStore store(1); @@ -90,8 +92,9 @@ TEST(SpriteStore, OtherPixelRatio) { } TEST(SpriteStore, Multiple) { - const auto sprite1 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); - const auto sprite2 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); + PremultipliedImage image(16, 16); + const auto sprite1 = std::make_shared(std::move(image), 2); + const auto sprite2 = std::make_shared(std::move(image), 2); using Sprites = std::map>; SpriteStore store(1); @@ -109,8 +112,9 @@ TEST(SpriteStore, Multiple) { TEST(SpriteStore, Replace) { FixtureLog log; - const auto sprite1 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); - const auto sprite2 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); + PremultipliedImage image(16, 16); + const auto sprite1 = std::make_shared(std::move(image), 2); + const auto sprite2 = std::make_shared(std::move(image), 2); using Sprites = std::map>; SpriteStore store(1); @@ -126,8 +130,10 @@ TEST(SpriteStore, Replace) { TEST(SpriteStore, ReplaceWithDifferentDimensions) { FixtureLog log; - const auto sprite1 = std::make_shared(16, 16, 2, std::string(16 * 16 * 4, '\0')); - const auto sprite2 = std::make_shared(18, 18, 2, std::string(18 * 18 * 4, '\0')); + PremultipliedImage image(16, 16); + PremultipliedImage image2(18, 18); + const auto sprite1 = std::make_shared(std::move(image), 2); + const auto sprite2 = std::make_shared(std::move(image2), 2); using Sprites = std::map>; SpriteStore store(1);