From 57fcae75b4a6c63b45bf7cb19def86b9e0cca5be Mon Sep 17 00:00:00 2001 From: Viktor Govako Date: Tue, 21 Jun 2022 08:26:09 +0300 Subject: [PATCH 1/3] [drape] Draw custom user marks in Bookmark. Signed-off-by: Viktor Govako --- drape/CMakeLists.txt | 2 + drape/drape_tests/glyph_packer_test.cpp | 15 ++- drape/font_texture.cpp | 75 +---------- drape/font_texture.hpp | 19 +-- drape/rect_packer.cpp | 70 ++++++++++ drape/rect_packer.hpp | 26 ++++ drape/symbols_texture.cpp | 136 +++++++++++++------- drape/symbols_texture.hpp | 108 ++++++++++++---- drape/texture_manager.cpp | 58 +++++++-- drape/texture_manager.hpp | 4 + drape_frontend/user_mark_shapes.cpp | 40 ++++-- drape_frontend/user_marks_provider.hpp | 12 +- map/api_mark_point.cpp | 2 +- map/bookmark.cpp | 13 +- map/routing_mark.cpp | 10 +- map/search_mark.cpp | 2 +- map/track_mark.cpp | 4 +- map/transit/transit_display.cpp | 14 +- map/user_mark.cpp | 2 +- xcode/drape/drape.xcodeproj/project.pbxproj | 8 ++ 20 files changed, 408 insertions(+), 212 deletions(-) create mode 100644 drape/rect_packer.cpp create mode 100644 drape/rect_packer.hpp diff --git a/drape/CMakeLists.txt b/drape/CMakeLists.txt index 1b19df7e8c..7ace0e8020 100644 --- a/drape/CMakeLists.txt +++ b/drape/CMakeLists.txt @@ -72,6 +72,8 @@ set(SRC overlay_tree.hpp pointers.cpp pointers.hpp + rect_packer.cpp + rect_packer.hpp render_bucket.cpp render_bucket.hpp render_state.cpp diff --git a/drape/drape_tests/glyph_packer_test.cpp b/drape/drape_tests/glyph_packer_test.cpp index 3004cd65f9..90c5dfb1cb 100644 --- a/drape/drape_tests/glyph_packer_test.cpp +++ b/drape/drape_tests/glyph_packer_test.cpp @@ -1,26 +1,27 @@ #include "testing/testing.hpp" -#include "drape/font_texture.hpp" + +#include "drape/rect_packer.hpp" UNIT_TEST(SimplePackTest) { - dp::GlyphPacker packer(m2::PointU(32, 32)); + dp::RectPacker packer(m2::PointU(32, 32)); m2::RectU r; - TEST(packer.PackGlyph(10, 13, r), ()); + TEST(packer.Pack(10, 13, r), ()); TEST_EQUAL(r, m2::RectU(0, 0, 10, 13), ()); - TEST(packer.PackGlyph(18, 8, r), ()); + TEST(packer.Pack(18, 8, r), ()); TEST_EQUAL(r, m2::RectU(10, 0, 28, 8), ()); - TEST(packer.PackGlyph(4, 15, r), ()); + TEST(packer.Pack(4, 15, r), ()); TEST_EQUAL(r, m2::RectU(28, 0, 32, 15), ()); - TEST(packer.PackGlyph(7, 10, r), ()); + TEST(packer.Pack(7, 10, r), ()); TEST(!packer.IsFull(), ()); TEST_EQUAL(r, m2::RectU(0, 15, 7, 25), ()); - TEST(!packer.PackGlyph(12, 18, r),()); + TEST(!packer.Pack(12, 18, r),()); TEST(packer.IsFull(), ()); TEST_EQUAL(r, m2::RectU(0, 15, 7, 25), ()); } diff --git a/drape/font_texture.cpp b/drape/font_texture.cpp index 25e0b30660..7215f4ef3c 100644 --- a/drape/font_texture.cpp +++ b/drape/font_texture.cpp @@ -15,75 +15,6 @@ namespace dp { -GlyphPacker::GlyphPacker(const m2::PointU & size) - : m_size(size) -{} - -bool GlyphPacker::PackGlyph(uint32_t width, uint32_t height, m2::RectU & rect) -{ - ASSERT_LESS(width, m_size.x, ()); - ASSERT_LESS(height, m_size.y, ()); - - if (m_cursor.x + width > m_size.x) - { - m_cursor.x = 0; - m_cursor.y += m_yStep; - m_yStep = 0; - } - - if (m_cursor.y + height > m_size.y) - { - m_isFull = true; - return false; - } - - rect = m2::RectU(m_cursor.x, m_cursor.y, - m_cursor.x + width, m_cursor.y + height); - - m_cursor.x += width; - m_yStep = std::max(height, m_yStep); - return true; -} - -bool GlyphPacker::CanBePacked(uint32_t glyphsCount, uint32_t width, uint32_t height) const -{ - uint32_t x = m_cursor.x; - uint32_t y = m_cursor.y; - uint32_t step = m_yStep; - for (uint32_t i = 0; i < glyphsCount; i++) - { - if (x + width > m_size.x) - { - x = 0; - y += step; - } - - if (y + height > m_size.y) - return false; - - x += width; - step = std::max(height, step); - } - return true; -} - -m2::RectF GlyphPacker::MapTextureCoords(const m2::RectU & pixelRect) const -{ - auto const width = static_cast(m_size.x); - auto const height = static_cast(m_size.y); - - // Half-pixel offset to eliminate artefacts on fetching from texture. - float offset = 0.0f; - if (pixelRect.SizeX() != 0 && pixelRect.SizeY() != 0) - offset = 0.5f; - - return {(pixelRect.minX() + offset) / width, - (pixelRect.minY() + offset) / height, - (pixelRect.maxX() - offset) / width, - (pixelRect.maxY() - offset) / height}; -} - -bool GlyphPacker::IsFull() const { return m_isFull; } GlyphIndex::GlyphIndex(m2::PointU const & size, ref_ptr mng, ref_ptr generator) @@ -164,7 +95,7 @@ ref_ptr GlyphIndex::MapResource(GlyphKey const & key, boo GlyphManager::Glyph glyph = m_mng->GetGlyph(key.GetUnicodePoint(), key.GetFixedSize()); m2::RectU r; - if (!m_packer.PackGlyph(glyph.m_image.m_width, glyph.m_image.m_height, r)) + if (!m_packer.Pack(glyph.m_image.m_width, glyph.m_image.m_height, r)) { glyph.m_image.Destroy(); if (glyph.m_metrics.m_isValid) @@ -241,10 +172,8 @@ void GlyphIndex::UploadResources(ref_ptr context, ref_ptr #include @@ -13,23 +14,7 @@ namespace dp { -class GlyphPacker -{ -public: - explicit GlyphPacker(m2::PointU const & size); - - bool PackGlyph(uint32_t width, uint32_t height, m2::RectU & rect); - bool CanBePacked(uint32_t glyphsCount, uint32_t width, uint32_t height) const; - m2::RectF MapTextureCoords(m2::RectU const & pixelRect) const; - bool IsFull() const; - m2::PointU const & GetSize() const { return m_size; } - -private: - m2::PointU m_size = m2::PointU(0, 0); - m2::PointU m_cursor = m2::PointU(0, 0); - uint32_t m_yStep = 0; - bool m_isFull = false; -}; +using GlyphPacker = RectPacker; class GlyphKey : public Texture::Key { diff --git a/drape/rect_packer.cpp b/drape/rect_packer.cpp new file mode 100644 index 0000000000..3429563d34 --- /dev/null +++ b/drape/rect_packer.cpp @@ -0,0 +1,70 @@ +#include "rect_packer.hpp" + +namespace dp +{ + +bool RectPacker::Pack(uint32_t width, uint32_t height, m2::RectU & rect) +{ + ASSERT_LESS(width, m_size.x, ()); + ASSERT_LESS(height, m_size.y, ()); + + if (m_cursor.x + width > m_size.x) + { + m_cursor.x = 0; + m_cursor.y += m_yStep; + m_yStep = 0; + } + + if (m_cursor.y + height > m_size.y) + { + m_isFull = true; + return false; + } + + rect = m2::RectU(m_cursor.x, m_cursor.y, + m_cursor.x + width, m_cursor.y + height); + + m_cursor.x += width; + m_yStep = std::max(height, m_yStep); + return true; +} + +bool RectPacker::CanBePacked(uint32_t glyphsCount, uint32_t width, uint32_t height) const +{ + uint32_t x = m_cursor.x; + uint32_t y = m_cursor.y; + uint32_t step = m_yStep; + for (uint32_t i = 0; i < glyphsCount; i++) + { + if (x + width > m_size.x) + { + x = 0; + y += step; + } + + if (y + height > m_size.y) + return false; + + x += width; + step = std::max(height, step); + } + return true; +} + +m2::RectF RectPacker::MapTextureCoords(const m2::RectU & pixelRect) const +{ + auto const width = static_cast(m_size.x); + auto const height = static_cast(m_size.y); + + // Half-pixel offset to eliminate artifacts on fetching from texture. + float offset = 0.0f; + if (pixelRect.SizeX() != 0 && pixelRect.SizeY() != 0) + offset = 0.5f; + + return {(pixelRect.minX() + offset) / width, + (pixelRect.minY() + offset) / height, + (pixelRect.maxX() - offset) / width, + (pixelRect.maxY() - offset) / height}; +} + +} // namespace dp diff --git a/drape/rect_packer.hpp b/drape/rect_packer.hpp new file mode 100644 index 0000000000..0c43f80a76 --- /dev/null +++ b/drape/rect_packer.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "geometry/rect2d.hpp" + +namespace dp +{ + +class RectPacker +{ +public: + explicit RectPacker(m2::PointU const & size) : m_size(size) {} + + bool Pack(uint32_t width, uint32_t height, m2::RectU & rect); + bool CanBePacked(uint32_t glyphsCount, uint32_t width, uint32_t height) const; + m2::RectF MapTextureCoords(m2::RectU const & pixelRect) const; + bool IsFull() const { return m_isFull; } + m2::PointU const & GetSize() const { return m_size; } + +private: + m2::PointU m_size; + m2::PointU m_cursor{0, 0}; + uint32_t m_yStep = 0; + bool m_isFull = false; +}; + +} // namespace dp diff --git a/drape/symbols_texture.cpp b/drape/symbols_texture.cpp index e78e480399..a22f230a49 100644 --- a/drape/symbols_texture.cpp +++ b/drape/symbols_texture.cpp @@ -4,9 +4,10 @@ #include "platform/platform.hpp" -#include "coding/reader.hpp" +#include "coding/file_reader.hpp" #include "coding/parse_xml.hpp" +#include "base/shared_buffer_manager.hpp" #include "base/string_utils.hpp" #include "3party/stb_image/stb_image.h" @@ -131,9 +132,8 @@ void LoadSymbols(std::string const & skinPathName, std::string const & textureNa DefinitionLoader loader(definitionInserter, convertToUV); { - ReaderPtr reader = - GetStyleReader().GetResourceReader(textureName + ".sdf", skinPathName); - ReaderSource> source(reader); + auto reader = GetStyleReader().GetResourceReader(textureName + ".sdf", skinPathName); + ReaderSource source(reader); if (!ParseXML(source, loader)) { failureHandler("Error parsing skin"); @@ -145,11 +145,10 @@ void LoadSymbols(std::string const & skinPathName, std::string const & textureNa } { - ReaderPtr reader = - GetStyleReader().GetResourceReader(textureName + ".png", skinPathName); + auto reader = GetStyleReader().GetResourceReader(textureName + ".png", skinPathName); size_t const size = static_cast(reader.Size()); rawData.resize(size); - reader.Read(0, &rawData[0], size); + reader.Read(0, rawData.data(), size); } } catch (RootException & e) @@ -159,8 +158,7 @@ void LoadSymbols(std::string const & skinPathName, std::string const & textureNa } int w, h, bpp; - unsigned char * data = - stbi_load_from_memory(&rawData[0], static_cast(rawData.size()), &w, &h, &bpp, 0); + unsigned char * data = stbi_load_from_memory(rawData.data(), static_cast(rawData.size()), &w, &h, &bpp, 0); ASSERT_EQUAL(bpp, 4, ("Incorrect symbols texture format")); ASSERT(glm::isPowerOfTwo(w), (w)); ASSERT(glm::isPowerOfTwo(h), (h)); @@ -178,29 +176,6 @@ void LoadSymbols(std::string const & skinPathName, std::string const & textureNa } } // namespace -SymbolsTexture::SymbolKey::SymbolKey(std::string const & symbolName) - : m_symbolName(symbolName) -{} - -Texture::ResourceType SymbolsTexture::SymbolKey::GetType() const -{ - return Texture::ResourceType::Symbol; -} - -std::string const & SymbolsTexture::SymbolKey::GetSymbolName() const -{ - return m_symbolName; -} - -SymbolsTexture::SymbolInfo::SymbolInfo(const m2::RectF & texRect) - : ResourceInfo(texRect) -{} - -Texture::ResourceType SymbolsTexture::SymbolInfo::GetType() const -{ - return Texture::ResourceType::Symbol; -} - SymbolsTexture::SymbolsTexture(ref_ptr context, std::string const & skinPathName, std::string const & textureName, ref_ptr allocator) : m_name(textureName) @@ -213,7 +188,7 @@ void SymbolsTexture::Load(ref_ptr context, std::string cons { auto definitionInserter = [this](std::string const & name, m2::RectF const & rect) { - m_definition.insert(std::make_pair(name, SymbolsTexture::SymbolInfo(rect))); + m_definition.emplace(name, SymbolInfo(rect)); }; auto completionHandler = [this, &allocator, context](unsigned char * data, uint32_t width, uint32_t height) @@ -256,15 +231,13 @@ void SymbolsTexture::Invalidate(ref_ptr context, std::strin ref_ptr SymbolsTexture::FindResource(Texture::Key const & key, bool & newResource) { - newResource = false; - if (key.GetType() != Texture::ResourceType::Symbol) - return nullptr; + ASSERT(key.GetType() == Texture::ResourceType::Symbol, ()); + newResource = false; std::string const & symbolName = static_cast(key).GetSymbolName(); auto it = m_definition.find(symbolName); - ASSERT(it != m_definition.end(), (symbolName)); - return make_ref(&it->second); + return (it != m_definition.end() ? make_ref(&it->second) : nullptr); } void SymbolsTexture::Fail(ref_ptr context) @@ -280,11 +253,6 @@ void SymbolsTexture::Fail(ref_ptr context) Create(context, p, make_ref(&alphaTexture)); } -bool SymbolsTexture::IsSymbolContained(std::string const & symbolName) const -{ - return m_definition.find(symbolName) != m_definition.end(); -} - bool SymbolsTexture::DecodeToMemory(std::string const & skinPathName, std::string const & textureName, std::vector & symbolsSkin, std::map & symbolsIndex, @@ -317,4 +285,86 @@ bool SymbolsTexture::DecodeToMemory(std::string const & skinPathName, std::strin definitionInserter, completionHandler, failureHandler); return result; } + +bool LoadedSymbol::FromPngFile(std::string const & filePath) +{ + std::vector buffer; + try + { + FileReader reader(filePath); + size_t const size = static_cast(reader.Size()); + buffer.resize(size); + reader.Read(0, buffer.data(), size); + } + catch (RootException & e) + { + return false; + } + + int bpp; + m_data = stbi_load_from_memory(buffer.data(), static_cast(buffer.size()), &m_width, &m_height, &bpp, 0); + if (m_data && bpp == 4) // only this fits TextureFormat::RGBA8 + return true; + + LOG(LWARNING, ("Error loading PNG for path:", filePath, "Result:", bool(m_data != nullptr), bpp)); + return false; +} + +void LoadedSymbol::Free() +{ + if (m_data) + { + stbi_image_free(m_data); + m_data = nullptr; + } +} + +ref_ptr SymbolsIndex::MapResource(SymbolKey const & key, bool & newResource) +{ + auto const & symbolName = key.GetSymbolName(); + + std::lock_guard guard(m_mapping); + auto it = m_index.find(symbolName); + if (it != m_index.end()) + { + newResource = false; + return make_ref(&it->second); + } + + LoadedSymbol symbol; + if (!symbol.FromPngFile(symbolName)) + return {}; + + newResource = true; + + m2::RectU pixelRect; + m_packer.Pack(symbol.m_width, symbol.m_height, pixelRect); + + { + std::lock_guard guard(m_upload); + m_pendingNodes.emplace_back(pixelRect, std::move(symbol)); + } + + auto res = m_index.emplace(symbolName, SymbolInfo(m_packer.MapTextureCoords(pixelRect))); + ASSERT(res.second, ()); + return make_ref(&res.first->second); +} + +void SymbolsIndex::UploadResources(ref_ptr context, ref_ptr texture) +{ + TPendingNodes pendingNodes; + { + std::lock_guard upload(m_upload); + if (m_pendingNodes.empty()) + return; + m_pendingNodes.swap(pendingNodes); + } + + for (auto const & [rect, symbol] : pendingNodes) + { + m2::PointU const zeroPoint = rect.LeftBottom(); + texture->UploadData(context, zeroPoint.x, zeroPoint.y, rect.SizeX(), rect.SizeY(), make_ref(symbol.m_data)); + } +} + } // namespace dp diff --git a/drape/symbols_texture.hpp b/drape/symbols_texture.hpp index 3a23985e81..a22b05ec0b 100644 --- a/drape/symbols_texture.hpp +++ b/drape/symbols_texture.hpp @@ -1,34 +1,36 @@ #pragma once -#include "drape/texture.hpp" +#include "drape/dynamic_texture.hpp" +#include "drape/rect_packer.hpp" #include +#include #include #include namespace dp { +class SymbolKey : public Texture::Key +{ +public: + explicit SymbolKey(std::string const & symbolName) : m_symbolName(symbolName) {} + Texture::ResourceType GetType() const override { return Texture::ResourceType::Symbol; } + std::string const & GetSymbolName() const { return m_symbolName; } + +private: + std::string m_symbolName; +}; + +class SymbolInfo : public Texture::ResourceInfo +{ +public: + explicit SymbolInfo(m2::RectF const & texRect) : Texture::ResourceInfo(texRect) {} + Texture::ResourceType GetType() const override { return Texture::ResourceType::Symbol; } +}; + class SymbolsTexture : public Texture { public: - class SymbolKey : public Key - { - public: - explicit SymbolKey(std::string const & symbolName); - ResourceType GetType() const override; - std::string const & GetSymbolName() const; - - private: - std::string m_symbolName; - }; - - class SymbolInfo : public ResourceInfo - { - public: - explicit SymbolInfo(m2::RectF const & texRect); - ResourceType GetType() const override; - }; - SymbolsTexture(ref_ptr context, std::string const & skinPathName, std::string const & textureName, ref_ptr allocator); @@ -40,8 +42,6 @@ public: ref_ptr allocator, std::vector> & internalTextures); - bool IsSymbolContained(std::string const & symbolName) const; - static bool DecodeToMemory(std::string const & skinPathName, std::string const & textureName, std::vector & symbolsSkin, std::map & symbolsIndex, @@ -51,8 +51,70 @@ private: void Load(ref_ptr context, std::string const & skinPathName, ref_ptr allocator); - using TSymDefinition = std::map; std::string m_name; - mutable TSymDefinition m_definition; + std::map m_definition; }; + +class LoadedSymbol +{ + DISALLOW_COPY(LoadedSymbol); + +public: + LoadedSymbol() = default; + LoadedSymbol(LoadedSymbol && rhs) + : m_data(rhs.m_data), m_width(rhs.m_width), m_height(rhs.m_height) + { + rhs.m_data = nullptr; + } + LoadedSymbol & operator=(LoadedSymbol &&) = delete; + + ~LoadedSymbol() + { + Free(); + } + + bool FromPngFile(std::string const & filePath); + void Free(); + + uint8_t * m_data = nullptr; + int m_width, m_height; +}; + +class SymbolsIndex +{ +public: + explicit SymbolsIndex(m2::PointU const & size) : m_packer(size) {} + + ref_ptr MapResource(SymbolKey const & key, bool & newResource); + void UploadResources(ref_ptr context, ref_ptr texture); + +private: + RectPacker m_packer; + + std::map m_index; + + using TPendingNodes = std::vector>; + TPendingNodes m_pendingNodes; + + std::mutex m_upload, m_mapping; +}; + +class DynamicSymbolsTexture : public DynamicTexture +{ + using TBase = DynamicTexture; + +public: + DynamicSymbolsTexture(m2::PointU const & size, ref_ptr allocator) + : m_index(size) + { + TBase::DynamicTextureParams params{size, TextureFormat::RGBA8, TextureFilter::Nearest, false /* m_usePixelBuffer */}; + TBase::Init(allocator, make_ref(&m_index), params); + } + + ~DynamicSymbolsTexture() override { TBase::Reset(); } + +private: + SymbolsIndex m_index; +}; + } // namespace dp diff --git a/drape/texture_manager.cpp b/drape/texture_manager.cpp index bcb8f0032b..c375775422 100644 --- a/drape/texture_manager.cpp +++ b/drape/texture_manager.cpp @@ -26,6 +26,7 @@ namespace dp namespace { uint32_t constexpr kMaxTextureSize = 1024; +uint32_t constexpr kUserSymbolsTextureSize = 512; uint32_t constexpr kStippleTextureWidth = 512; /// @todo Should be equal with kMaxStipplePenLength? uint32_t constexpr kMinStippleTextureHeight = 64; uint32_t constexpr kMinColorTextureSize = 32; @@ -67,6 +68,12 @@ void ParseColorsList(std::string const & colorsFile, ToDo toDo) } } +m2::PointU UserSymbolsTextureSize(uint32_t maxTextureSize) +{ + uint32_t const sz = std::min(kUserSymbolsTextureSize, maxTextureSize); + return {sz, sz}; +} + m2::PointU StipplePenTextureSize(size_t patternsCount, uint32_t maxTextureSize) { uint32_t const sz = base::NextPowOf2(static_cast(patternsCount) + kReservedPatterns); @@ -206,6 +213,7 @@ void TextureManager::Release() m_hybridGlyphGroups.clear(); m_symbolTextures.clear(); + m_userSymbolTexture.reset(); m_stipplePenTexture.reset(); m_colorTexture.reset(); @@ -254,6 +262,9 @@ bool TextureManager::UpdateDynamicTextures(ref_ptr context) CHECK(m_stipplePenTexture != nullptr, ()); m_stipplePenTexture->UpdateState(context); + CHECK(m_userSymbolTexture != nullptr, ()); + m_userSymbolTexture->UpdateState(context); + UpdateGlyphTextures(context); CHECK(m_textureAllocator != nullptr, ()); @@ -285,15 +296,23 @@ ref_ptr TextureManager::AllocateGlyphTexture() return make_ref(m_glyphTextures.back()); } -void TextureManager::GetRegionBase(ref_ptr tex, TextureManager::BaseRegion & region, - Texture::Key const & key) +bool TextureManager::GetRegionSafe(ref_ptr tex, TextureManager::BaseRegion & region, Texture::Key const & key) { - bool isNew = false; - region.SetResourceInfo(tex != nullptr ? tex->FindResource(key, isNew) : nullptr); + bool isNew; + auto const info = tex->FindResource(key, isNew); + if (!info) + return false; + + region.SetResourceInfo(info); region.SetTexture(tex); - ASSERT(region.IsValid(), ()); if (isNew) m_nothingToUpload.clear(); + return true; +} + +void TextureManager::GetRegionBase(ref_ptr tex, TextureManager::BaseRegion & region, Texture::Key const & key) +{ + VERIFY(GetRegionSafe(tex, region, key), ()); } void TextureManager::GetGlyphsRegions(ref_ptr tex, strings::UniString const & text, @@ -410,6 +429,10 @@ void TextureManager::Init(ref_ptr context, Params const & p make_ref(m_textureAllocator))); } + m_userSymbolTexture = make_unique_dp(UserSymbolsTextureSize(m_maxTextureSize), + make_ref(m_textureAllocator)); + LOG(LDEBUG, ("User symbols texture size =", m_userSymbolTexture->GetWidth(), m_userSymbolTexture->GetHeight())); + // Initialize static textures. m_trafficArrowTexture = make_unique_dp(context, "traffic-arrow.png", m_resPostfix, @@ -542,20 +565,33 @@ void TextureManager::GetTexturesToCleanup(std::vector> & te bool TextureManager::GetSymbolRegionSafe(std::string const & symbolName, SymbolRegion & region) { CHECK(m_isInitialized, ()); - for (size_t i = 0; i < m_symbolTextures.size(); ++i) + ASSERT(!symbolName.empty(), ()); + + SymbolKey const key(symbolName); + for (uint32_t i = 0; i < m_symbolTextures.size(); ++i) { - ref_ptr symbolsTexture = make_ref(m_symbolTextures[i]); - ASSERT(symbolsTexture != nullptr, ()); - if (symbolsTexture->IsSymbolContained(symbolName)) + if (GetRegionSafe(make_ref(m_symbolTextures[i]), region, key)) { - GetRegionBase(symbolsTexture, region, SymbolsTexture::SymbolKey(symbolName)); - region.SetTextureIndex(static_cast(i)); + region.SetTextureIndex(i); return true; } } return false; } +bool TextureManager::GetUserSymbolRegion(std::string const & symbolName, SymbolRegion & region) +{ + CHECK(m_isInitialized, ()); + ASSERT(!symbolName.empty(), ()); + + if (GetRegionSafe(make_ref(m_userSymbolTexture), region, SymbolKey(symbolName))) + { + region.SetTextureIndex(m_symbolTextures.size()); + return true; + } + return false; +} + void TextureManager::GetSymbolRegion(std::string const & symbolName, SymbolRegion & region) { if (!GetSymbolRegionSafe(symbolName, region)) diff --git a/drape/texture_manager.hpp b/drape/texture_manager.hpp index 06bfe2d5f4..a24a2be5e2 100644 --- a/drape/texture_manager.hpp +++ b/drape/texture_manager.hpp @@ -92,6 +92,7 @@ public: bool GetSymbolRegionSafe(std::string const & symbolName, SymbolRegion & region); void GetSymbolRegion(std::string const & symbolName, SymbolRegion & region); + bool GetUserSymbolRegion(std::string const & symbolName, SymbolRegion & region); void GetStippleRegion(PenPatternT const & pen, StippleRegion & region); void GetColorRegion(Color const & color, ColorRegion & region); @@ -155,6 +156,7 @@ private: uint32_t m_maxGlypsCount; ref_ptr AllocateGlyphTexture(); + bool GetRegionSafe(ref_ptr tex, TextureManager::BaseRegion & region, Texture::Key const & key); void GetRegionBase(ref_ptr tex, TextureManager::BaseRegion & region, Texture::Key const & key); void GetGlyphsRegions(ref_ptr tex, strings::UniString const & text, @@ -225,7 +227,9 @@ private: bool m_isInitialized = false; ref_ptr m_glyphGenerator; std::string m_resPostfix; + std::vector> m_symbolTextures; + drape_ptr m_userSymbolTexture; drape_ptr m_stipplePenTexture; drape_ptr m_colorTexture; std::list> m_glyphTextures; diff --git a/drape_frontend/user_mark_shapes.cpp b/drape_frontend/user_mark_shapes.cpp index 3aede7376d..004e362224 100644 --- a/drape_frontend/user_mark_shapes.cpp +++ b/drape_frontend/user_mark_shapes.cpp @@ -21,6 +21,8 @@ #include "geometry/clipping.hpp" #include "geometry/mercator.hpp" +#include "base/file_name_utils.hpp" + #include #include #include @@ -118,10 +120,11 @@ std::string GetSymbolNameForZoomLevel(ref_ptr if (!symbolNames) return {}; - for (auto itName = symbolNames->crbegin(); itName != symbolNames->crend(); ++itName) + auto const & v = symbolNames->m_zoomInfo; + for (auto it = v.crbegin(); it != v.crend(); ++it) { - if (itName->first <= tileKey.m_zoomLevel) - return itName->second; + if (it->first <= tileKey.m_zoomLevel) + return it->second; } return {}; } @@ -397,24 +400,34 @@ void CacheUserMarks(ref_ptr context, TileKey const & tileKe m2::PointD const tileCenter = tileKey.GetGlobalRect().Center(); + // Calculate symbol's region params. m2::PointF symbolSize(0.0f, 0.0f); dp::TextureManager::SymbolRegion symbolRegion; - auto const symbolName = GetSymbolNameForZoomLevel(make_ref(renderInfo.m_symbolNames), tileKey); + auto symbolName = GetSymbolNameForZoomLevel(make_ref(renderInfo.m_symbolNames), tileKey); if (!symbolName.empty()) { - textures->GetSymbolRegion(symbolName, symbolRegion); + if (!textures->GetSymbolRegionSafe(symbolName, symbolRegion)) + { + if (renderInfo.m_symbolNames->m_pathPrefix.empty() || + !textures->GetUserSymbolRegion(base::JoinPath(renderInfo.m_symbolNames->m_pathPrefix, symbolName), symbolRegion)) + { + LOG(LWARNING, ("Can't load symbol:", symbolName)); + + // Fallback to the default bookmark's icon if user icon's file was not loaded. + symbolName = "bookmark-default-m"; + VERIFY(textures->GetSymbolRegionSafe(symbolName, symbolRegion), ()); + } + } + symbolSize = symbolRegion.GetPixelSize(); } - m2::PointF symbolOffset = m2::PointF::Zero(); + m2::PointF symbolOffset(0, 0); if (renderInfo.m_symbolIsPOI) symbolOffset = GetSymbolOffsetForZoomLevel(make_ref(renderInfo.m_symbolOffsets), tileKey); if (renderInfo.m_coloredSymbols != nullptr) - { - GenerateColoredSymbolShapes(context, textures, renderInfo, tileKey, tileCenter, symbolOffset, symbolSize, - batcher); - } + GenerateColoredSymbolShapes(context, textures, renderInfo, tileKey, tileCenter, symbolOffset, symbolSize, batcher); if (!symbolName.empty()) { @@ -437,8 +450,7 @@ void CacheUserMarks(ref_ptr context, TileKey const & tileKe m2::RectF const & bgTexRect = backgroundRegion.GetTexRect(); m2::PointF const pxSize = symbolRegion.GetPixelSize(); dp::Anchor const anchor = renderInfo.m_anchor; - m2::PointD const pt = MapShape::ConvertToLocal(renderInfo.m_pivot, tileCenter, - kShapeCoordScalar); + m2::PointD const pt = MapShape::ConvertToLocal(renderInfo.m_pivot, tileCenter, kShapeCoordScalar); glsl::vec3 const pos = glsl::vec3(glsl::ToVec2(pt), renderInfo.m_depth); bool const runAnim = renderInfo.m_hasCreationAnimation && renderInfo.m_justCreated; @@ -449,12 +461,12 @@ void CacheUserMarks(ref_ptr context, TileKey const & tileKe m2::PointD const pixelOffset = renderInfo.m_pixelOffset; glsl::vec2 const offset(pixelOffset.x, pixelOffset.y); + /// @todo Here we always have maskColor, which may conflict with custom user symbols. dp::Color color = dp::Color::White(); if (!renderInfo.m_color.empty()) color = df::GetColorConstant(renderInfo.m_color); - glsl::vec4 maskColor(color.GetRedF(), color.GetGreenF(), color.GetBlueF(), - renderInfo.m_symbolOpacity); + glsl::vec4 const maskColor(color.GetRedF(), color.GetGreenF(), color.GetBlueF(), renderInfo.m_symbolOpacity); float animateOrZ = 0.0f; if (!renderInfo.m_customDepth) animateOrZ = runAnim ? 1.0f : -1.0f; diff --git a/drape_frontend/user_marks_provider.hpp b/drape_frontend/user_marks_provider.hpp index 2a799ceea9..f66bd87d67 100644 --- a/drape_frontend/user_marks_provider.hpp +++ b/drape_frontend/user_marks_provider.hpp @@ -38,10 +38,20 @@ struct IDCollections class UserPointMark { public: - using SymbolNameZoomInfo = std::map; + struct SymbolNameZoomInfo + { + std::map m_zoomInfo; + // Check symbol from file if not empty. + std::string m_pathPrefix; + + bool IsEmpty() const { return m_zoomInfo.empty(); } + void Emplace(int zoom, std::string name) { m_zoomInfo.emplace(zoom, std::move(name)); } + }; + using TitlesInfo = std::vector; using SymbolSizes = std::vector; using SymbolOffsets = std::vector; + struct ColoredSymbolZoomInfo { std::map m_zoomInfo; diff --git a/map/api_mark_point.cpp b/map/api_mark_point.cpp index 5793a6f30e..6c3b58baed 100644 --- a/map/api_mark_point.cpp +++ b/map/api_mark_point.cpp @@ -50,7 +50,7 @@ drape_ptr ApiMarkPoint::GetSymbolNames() { //TODO: use its own icon. auto symbol = make_unique_dp(); - symbol->insert(std::make_pair(1 /* zoomLevel */, "coloredmark-default-s")); + symbol->Emplace(1, "coloredmark-default-s"); return symbol; } diff --git a/map/bookmark.cpp b/map/bookmark.cpp index 2ac03deae3..286bec53b2 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -109,10 +109,9 @@ drape_ptr Bookmark::GetSymbolNames() cons symbolNames = make_unique_dp(); - symbolNames->insert(std::make_pair(1 /* zoomLevel */, "bookmark-default-xs")); - symbolNames->insert(std::make_pair(8 /* zoomLevel */, "bookmark-default-s")); - auto const iconType = GetBookmarkIconType(m_data.m_icon); - symbolNames->insert(std::make_pair(14 /* zoomLevel */, "bookmark-" + iconType + "-m")); + symbolNames->Emplace(1, "bookmark-default-xs"); + symbolNames->Emplace(8, "bookmark-default-s"); + symbolNames->Emplace(14, "bookmark-" + GetBookmarkIconType(m_data.m_icon) + "-m"); return symbolNames; } @@ -123,6 +122,8 @@ drape_ptr Bookmark::GetCustomSymbolNames( return nullptr; auto symbolNames = make_unique_dp(); + symbolNames->m_pathPrefix = GetBookmarksDirectory(); + strings::Tokenize(it->second, ";", [&](std::string_view token) { uint8_t zoomLevel = 1; @@ -130,10 +131,10 @@ drape_ptr Bookmark::GetCustomSymbolNames( if (pos != std::string::npos && strings::to_uint(token.substr(0, pos), zoomLevel)) token = token.substr(pos + 1); if (!token.empty() && zoomLevel >= 1 && zoomLevel <= scales::GetUpperStyleScale()) - symbolNames->emplace(zoomLevel, std::string(token)); + symbolNames->Emplace(zoomLevel, std::string(token)); }); - if (symbolNames->empty()) + if (symbolNames->IsEmpty()) return nullptr; return symbolNames; diff --git a/map/routing_mark.cpp b/map/routing_mark.cpp index 01b651645f..5e51ed71de 100644 --- a/map/routing_mark.cpp +++ b/map/routing_mark.cpp @@ -195,7 +195,7 @@ drape_ptr RouteMarkPoint::GetSymbolNames( } } auto symbol = make_unique_dp(); - symbol->insert(std::make_pair(1 /* zoomLevel */, name)); + symbol->Emplace(1, std::move(name)); return symbol; } @@ -470,7 +470,7 @@ drape_ptr TransitMark::GetTitleDecl() const return titles; } -void TransitMark::SetSymbolNames(std::map const & symbolNames) +void TransitMark::SetSymbolNames(df::UserPointMark::SymbolNameZoomInfo const & symbolNames) { SetDirty(); m_symbolNames = symbolNames; @@ -525,7 +525,7 @@ dp::Anchor TransitMark::GetAnchor() const drape_ptr TransitMark::GetSymbolNames() const { - if (m_symbolNames.empty()) + if (m_symbolNames.IsEmpty()) return nullptr; return make_unique_dp(m_symbolNames); } @@ -552,7 +552,7 @@ SpeedCameraMark::SpeedCameraMark(m2::PointD const & ptOrg) m_titleDecl.m_primaryOffset.x = kSpeedCameraOutlineWidth + kSpeedCameraMarkTextMargin; m_titleDecl.m_anchor = dp::Left; - m_symbolNames.insert(std::make_pair(kMinSpeedCameraZoom, "speedcam-alert-l")); + m_symbolNames.Emplace(kMinSpeedCameraZoom, "speedcam-alert-l"); df::ColoredSymbolViewParams params; params.m_color = df::GetColorConstant(kSpeedCameraMarkBg); @@ -674,7 +674,7 @@ drape_ptr RoadWarningMark::GetSymbolNames case RoadWarningMarkType::Count: CHECK(false, ()); break; } auto symbol = make_unique_dp(); - symbol->insert(std::make_pair(1 /* zoomLevel */, symbolName)); + symbol->Emplace(1, std::move(symbolName)); return symbol; } diff --git a/map/search_mark.cpp b/map/search_mark.cpp index f2573eda8e..e119363d7e 100644 --- a/map/search_mark.cpp +++ b/map/search_mark.cpp @@ -212,7 +212,7 @@ drape_ptr SearchMarkPoint::GetSymbolNames return nullptr; auto symbolZoomInfo = make_unique_dp(); - symbolZoomInfo->emplace(1 /*kWorldZoomLevel*/, *symbolName); + symbolZoomInfo->Emplace(1, *symbolName); return symbolZoomInfo; } diff --git a/map/track_mark.cpp b/map/track_mark.cpp index e960238b9f..7a9f03fe9e 100644 --- a/map/track_mark.cpp +++ b/map/track_mark.cpp @@ -40,7 +40,7 @@ void TrackInfoMark::SetTrackId(kml::TrackId trackId) drape_ptr TrackInfoMark::GetSymbolNames() const { auto symbol = make_unique_dp(); - symbol->insert(std::make_pair(kMinInfoVisibleZoom, kInfoSignSymbolName)); + symbol->Emplace(kMinInfoVisibleZoom, kInfoSignSymbolName); return symbol; } @@ -91,7 +91,7 @@ void TrackSelectionMark::SetMyPositionDistance(double distance) drape_ptr TrackSelectionMark::GetSymbolNames() const { auto symbol = make_unique_dp(); - symbol->insert(std::make_pair(m_minVisibleZoom, kTrackDeselectedSymbolName)); + symbol->Emplace(m_minVisibleZoom, kTrackDeselectedSymbolName); return symbol; } diff --git a/map/transit/transit_display.cpp b/map/transit/transit_display.cpp index 2ec4e7f87c..9b0e8bc779 100644 --- a/map/transit/transit_display.cpp +++ b/map/transit/transit_display.cpp @@ -836,8 +836,8 @@ void TransitRouteDisplay::CreateTransitMarks() return name != kZeroIcon ? name + suffix : name; }; df::UserPointMark::SymbolNameZoomInfo symbolNames; - symbolNames[kSmallIconZoom] = GetSymbolName(mark.m_symbolName, "-s"); - symbolNames[kMediumIconZoom] = GetSymbolName(mark.m_symbolName, "-m"); + symbolNames.Emplace(kSmallIconZoom, GetSymbolName(mark.m_symbolName, "-s")); + symbolNames.Emplace(kMediumIconZoom, GetSymbolName(mark.m_symbolName, "-m")); transitMark->SetSymbolNames(symbolNames); transitMark->SetPriority(UserMark::Priority::TransitGate); @@ -864,7 +864,7 @@ void TransitRouteDisplay::CreateTransitMarks() titleTransitMark->AddTitle(titleDecl); titleTransitMark->SetAnchor(dp::Top); - titleTransitMark->SetSymbolNames({{1 /* minZoom */, "transfer_arrow"}}); + titleTransitMark->SetSymbolNames({{{1 /* minZoom */, "transfer_arrow"}}, {}}); titleTransitMark->SetSymbolOffsets(transferArrowOffsets); titleTransitMark->SetPriority(UserMark::Priority::TransitTransfer); } @@ -900,19 +900,19 @@ void TransitRouteDisplay::CreateTransitMarks() if (mark.m_type == TransitMarkInfo::Type::KeyStop) { df::UserPointMark::SymbolNameZoomInfo symbolNames; - symbolNames[kSmallIconZoom] = mark.m_symbolName + "-s"; - symbolNames[kMediumIconZoom] = mark.m_symbolName + "-m"; + symbolNames.Emplace(kSmallIconZoom, mark.m_symbolName + "-s"); + symbolNames.Emplace(kMediumIconZoom, mark.m_symbolName + "-m"); transitMark->SetSymbolNames(symbolNames); df::UserPointMark::ColoredSymbolZoomInfo coloredSymbol; df::ColoredSymbolViewParams params; params.m_color = df::GetColorConstant(mark.m_color); - auto sz = m_symbolSizes.at(symbolNames[kSmallIconZoom]); + auto sz = m_symbolSizes.at(symbolNames.m_zoomInfo.at(kSmallIconZoom)); params.m_radiusInPixels = max(sz.x, sz.y) * kGateBgScale * 0.5f; coloredSymbol.m_zoomInfo[kSmallIconZoom] = params; - sz = m_symbolSizes.at(symbolNames[kMediumIconZoom]); + sz = m_symbolSizes.at(symbolNames.m_zoomInfo.at(kMediumIconZoom)); params.m_radiusInPixels = max(sz.x, sz.y) * kGateBgScale * 0.5f; coloredSymbol.m_zoomInfo[kMediumIconZoom] = params; diff --git a/map/user_mark.cpp b/map/user_mark.cpp index 23a0fe3275..164562bd33 100644 --- a/map/user_mark.cpp +++ b/map/user_mark.cpp @@ -56,7 +56,7 @@ DebugMarkPoint::DebugMarkPoint(const m2::PointD & ptOrg) drape_ptr DebugMarkPoint::GetSymbolNames() const { auto symbol = make_unique_dp(); - symbol->insert(std::make_pair(1 /* zoomLevel */, "non-found-search-result")); + symbol->Emplace(1, "non-found-search-result"); return symbol; } diff --git a/xcode/drape/drape.xcodeproj/project.pbxproj b/xcode/drape/drape.xcodeproj/project.pbxproj index 3236af8526..ecb8a69d54 100644 --- a/xcode/drape/drape.xcodeproj/project.pbxproj +++ b/xcode/drape/drape.xcodeproj/project.pbxproj @@ -139,6 +139,8 @@ 6743D3451C3533AE0095054B /* support_manager.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 6743D3431C3533AE0095054B /* support_manager.hpp */; }; 675D21991BFB876E00717E4F /* projection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 675D21971BFB876E00717E4F /* projection.cpp */; }; 675D219A1BFB876E00717E4F /* projection.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 675D21981BFB876E00717E4F /* projection.hpp */; }; + ACA9AB4D285F8BA4009FFFCD /* rect_packer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = ACA9AB4B285F8BA4009FFFCD /* rect_packer.cpp */; }; + ACA9AB4E285F8BA4009FFFCD /* rect_packer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = ACA9AB4C285F8BA4009FFFCD /* rect_packer.hpp */; }; BB035F6C1E3A2A5C00519962 /* drape_diagnostics.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BB035F6B1E3A2A5C00519962 /* drape_diagnostics.hpp */; }; BBAD59F821258812005543FC /* debug_renderer.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BBAD59F721258812005543FC /* debug_renderer.hpp */; }; BBB72E902110AF0F00249D4F /* oglcontext.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BBB72E8F2110AF0F00249D4F /* oglcontext.cpp */; }; @@ -287,6 +289,8 @@ 6743D3431C3533AE0095054B /* support_manager.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = support_manager.hpp; sourceTree = ""; }; 675D21971BFB876E00717E4F /* projection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = projection.cpp; sourceTree = ""; }; 675D21981BFB876E00717E4F /* projection.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = projection.hpp; sourceTree = ""; }; + ACA9AB4B285F8BA4009FFFCD /* rect_packer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rect_packer.cpp; sourceTree = ""; }; + ACA9AB4C285F8BA4009FFFCD /* rect_packer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = rect_packer.hpp; sourceTree = ""; }; BB035F6B1E3A2A5C00519962 /* drape_diagnostics.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = drape_diagnostics.hpp; sourceTree = ""; }; BBAD59F721258812005543FC /* debug_renderer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = debug_renderer.hpp; sourceTree = ""; }; BBB72E8F2110AF0F00249D4F /* oglcontext.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = oglcontext.cpp; sourceTree = ""; }; @@ -456,6 +460,8 @@ 6729A5331A69213A007D5872 /* overlay_tree.hpp */, 6729A5341A69213A007D5872 /* pointers.cpp */, 6729A5351A69213A007D5872 /* pointers.hpp */, + ACA9AB4B285F8BA4009FFFCD /* rect_packer.cpp */, + ACA9AB4C285F8BA4009FFFCD /* rect_packer.hpp */, 6729A5361A69213A007D5872 /* render_bucket.cpp */, 6729A5371A69213A007D5872 /* render_bucket.hpp */, 45D7ADE12113535600160DE3 /* render_state.cpp */, @@ -551,6 +557,7 @@ 4560F5A92142A17B00CC736C /* metal_gpu_buffer_impl.hpp in Headers */, 45789EDA2133E14F009955CC /* metal_base_context.hpp in Headers */, 6743D3451C3533AE0095054B /* support_manager.hpp in Headers */, + ACA9AB4E285F8BA4009FFFCD /* rect_packer.hpp in Headers */, 6729A5B31A69213A007D5872 /* vertex_decl.hpp in Headers */, 6729A5721A69213A007D5872 /* cpu_buffer.hpp in Headers */, 4525F88522154BE800CAC51A /* vulkan_pipeline.hpp in Headers */, @@ -678,6 +685,7 @@ 457B536720358F7E00E4E752 /* glyph_generator.cpp in Sources */, 6729A5A21A69213A007D5872 /* stipple_pen_resource.cpp in Sources */, 6729A5691A69213A007D5872 /* batcher.cpp in Sources */, + ACA9AB4D285F8BA4009FFFCD /* rect_packer.cpp in Sources */, 670947251BDF9A4F005014C0 /* bidi.cpp in Sources */, 4577B25D21F2035D00864FAC /* vulkan_layers.cpp in Sources */, 6729A58C1A69213A007D5872 /* index_buffer_mutator.cpp in Sources */, -- 2.45.3 From 906a98ef7b9e7d856a74abbeb6b7a1cf3eb0b4b6 Mon Sep 17 00:00:00 2001 From: Viktor Govako Date: Tue, 21 Jun 2022 15:08:04 +0300 Subject: [PATCH 2/3] [bookmarks] Import kmz with custom user symbols. Signed-off-by: Viktor Govako --- .../UI/EditBookmark/BookmarkUIUtils.swift | 4 +- kml/serdes.cpp | 211 +++++++++--------- kml/serdes.hpp | 36 ++- kml/types.hpp | 4 + map/bookmark.cpp | 39 +++- map/bookmark.hpp | 5 +- map/bookmark_helpers.cpp | 36 ++- map/bookmark_manager.cpp | 14 ++ map/map_tests/CMakeLists.txt | 1 + map/map_tests/kmz_unarchive_test.cpp | 50 +++-- .../writable_dir_changer.cpp | 14 +- .../writable_dir_changer.hpp | 10 +- qt/bookmark_dialog.cpp | 6 +- 13 files changed, 262 insertions(+), 168 deletions(-) diff --git a/iphone/Maps/UI/EditBookmark/BookmarkUIUtils.swift b/iphone/Maps/UI/EditBookmark/BookmarkUIUtils.swift index 1c12eb55d4..ccca47cd7c 100644 --- a/iphone/Maps/UI/EditBookmark/BookmarkUIUtils.swift +++ b/iphone/Maps/UI/EditBookmark/BookmarkUIUtils.swift @@ -73,7 +73,9 @@ fileprivate func uiColorForBookmarkColor(_ color: BookmarkColor) -> UIColor { case .gray: return rgbColor(115, 115, 115); case .blueGray: return rgbColor(89, 115, 128); case .none, .count: - fatalError() + // Clear color (transparent) for custom symbols. + /// @todo Rewrite and show custom images in Bookmarls view. + return UIColor.clear @unknown default: fatalError() } diff --git a/kml/serdes.cpp b/kml/serdes.cpp index 3be716984d..5f8f646ba6 100644 --- a/kml/serdes.cpp +++ b/kml/serdes.cpp @@ -100,8 +100,7 @@ PredefinedColor ExtractPlacemarkPredefinedColor(std::string const & s) if (s == "#placemark-bluegray") return PredefinedColor::BlueGray; - // Default color. - return PredefinedColor::Red; + return PredefinedColor::None; } std::string GetStyleForPredefinedColor(PredefinedColor color) @@ -174,8 +173,7 @@ void SaveStringWithCDATA(KmlWriter::WriterWrapper & writer, std::string s) writer << s; } -void SaveStyle(KmlWriter::WriterWrapper & writer, std::string const & style, - std::string_view const & indent) +void SaveStyle(KmlWriter::WriterWrapper & writer, std::string const & style, std::string_view const & indent) { if (style.empty()) return; @@ -183,7 +181,7 @@ void SaveStyle(KmlWriter::WriterWrapper & writer, std::string const & style, writer << indent << kIndent2 << "\n"; @@ -262,10 +260,6 @@ void SaveStringsMap(KmlWriter::WriterWrapper & writer, writer << indent << "\n"; } -void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData, - std::string const & extendedServerId, - std::vector const * compilationData); - void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData, std::string const & extendedServerId, std::vector const * compilationData) @@ -344,9 +338,8 @@ void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData co if (compilationData) { - for (auto const & compilationDatum : *compilationData) - SaveCategoryData(writer, compilationDatum, {} /* extendedServerId */, - nullptr /* compilationData */); + for (auto const & cd : *compilationData) + SaveCategoryExtendedData(writer, cd, {} /* extendedServerId */, nullptr /* compilationData */); } if (compilationData) @@ -357,29 +350,23 @@ void SaveCategoryExtendedData(KmlWriter::WriterWrapper & writer, CategoryData co void SaveCategoryData(KmlWriter::WriterWrapper & writer, CategoryData const & categoryData, std::string const & extendedServerId, - std::vector const * compilationData) + std::vector const & compilationData) { - if (compilationData) + // Use CDATA if we have special symbols in the name. + writer << kIndent2 << ""; + SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_name, kDefaultLang)); + writer << "\n"; + + if (!categoryData.m_description.empty()) { - for (uint8_t i = 0; i < base::Underlying(PredefinedColor::Count); ++i) - SaveStyle(writer, GetStyleForPredefinedColor(static_cast(i)), kIndent0); - - // Use CDATA if we have special symbols in the name. - writer << kIndent2 << ""; - SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_name, kDefaultLang)); - writer << "\n"; - - if (!categoryData.m_description.empty()) - { - writer << kIndent2 << ""; - SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_description, kDefaultLang)); - writer << "\n"; - } - - writer << kIndent2 << "" << (categoryData.m_visible ? "1" : "0") << "\n"; + writer << kIndent2 << ""; + SaveStringWithCDATA(writer, GetLocalizableString(categoryData.m_description, kDefaultLang)); + writer << "\n"; } - SaveCategoryExtendedData(writer, categoryData, extendedServerId, compilationData); + writer << kIndent2 << "" << (categoryData.m_visible ? "1" : "0") << "\n"; + + SaveCategoryExtendedData(writer, categoryData, extendedServerId, &compilationData); } void SaveBookmarkExtendedData(KmlWriter::WriterWrapper & writer, BookmarkData const & bookmarkData) @@ -475,7 +462,9 @@ void SaveBookmarkData(KmlWriter::WriterWrapper & writer, BookmarkData const & bo << "\n"; } - auto const style = GetStyleForPredefinedColor(bookmarkData.m_color.m_predefinedColor); + auto style = bookmarkData.m_iconPath; + if (style.empty()) + style = GetStyleForPredefinedColor(bookmarkData.m_color.m_predefinedColor); writer << kIndent4 << "#" << style << "\n" << kIndent4 << "" << PointToString(bookmarkData.m_point) << "\n"; @@ -649,9 +638,23 @@ void KmlWriter::Write(FileData const & fileData) { m_writer << kKmlHeader; + // Save predefined styles. + for (uint8_t i = 0; i < base::Underlying(PredefinedColor::Count); ++i) + { + auto const style = GetStyleForPredefinedColor(static_cast(i)); + if (!style.empty()) + SaveStyle(m_writer, "https://omaps.app/placemarks/" + style + ".png", kIndent0); + } + + // Save user styles. + for (auto const & bd : fileData.m_bookmarksData) + { + if (!bd.m_iconPath.empty()) + SaveStyle(m_writer, bd.m_iconPath, kIndent0); + } + // Save category. - SaveCategoryData(m_writer, fileData.m_categoryData, fileData.m_serverId, - &fileData.m_compilationsData); + SaveCategoryData(m_writer, fileData.m_categoryData, fileData.m_serverId, fileData.m_compilationsData); // Save bookmarks. for (auto const & bookmarkData : fileData.m_bookmarksData) @@ -677,15 +680,18 @@ void KmlParser::ResetPoint() m_name.clear(); m_description.clear(); m_org = {}; - m_predefinedColor = PredefinedColor::None; m_viewportScale = 0; m_timestamp = {}; - m_color = 0; + m_currStyle.Invalidate(); m_styleId.clear(); m_mapStyleId.clear(); m_styleUrlKey.clear(); + m_predefinedColor = PredefinedColor::None; + m_icon = BookmarkIcon::None; + m_iconPath.clear(); + m_featureTypes.clear(); m_customName.clear(); m_boundTracks.clear(); @@ -695,8 +701,6 @@ void KmlParser::ResetPoint() m_properties.clear(); m_localId = 0; m_trackLayers.clear(); - m_trackWidth = kDefaultTrackWidth; - m_icon = BookmarkIcon::None; m_geometry.Clear(); m_geometryType = GEOMETRY_TYPE_UNKNOWN; @@ -751,13 +755,19 @@ bool KmlParser::MakeValid() if (m_name.empty() && m_featureTypes.empty()) m_name[kDefaultLang] = PointToString(m_org); - // Set default pin. - if (m_predefinedColor == PredefinedColor::None) + if (m_predefinedColor != PredefinedColor::None) + { + // We use fixed predefined colors instead of their path like "https://omaps.app/placemarks/placemark-red.png". + m_iconPath.clear(); + } + else if (m_iconPath.empty()) + { + // Set default color if there is no icon path. m_predefinedColor = PredefinedColor::Red; + } return true; } - return false; } else if (GEOMETRY_TYPE_LINE == m_geometryType) { @@ -774,35 +784,32 @@ void KmlParser::ParseColor(std::string const & value) return; // Color positions in HEX – aabbggrr. - m_color = ToRGBA(fromHex[3], fromHex[2], fromHex[1], fromHex[0]); + m_currStyle.color = ToRGBA(fromHex[3], fromHex[2], fromHex[1], fromHex[0]); } -bool KmlParser::GetColorForStyle(std::string const & styleUrl, uint32_t & color) const +KmlParser::StyleParams const * KmlParser::GetStyle(std::string styleUrl) const { if (styleUrl.empty()) - return false; + return nullptr; - // Remove leading '#' symbol - auto const it = m_styleUrl2Color.find(styleUrl.substr(1)); - if (it != m_styleUrl2Color.cend()) + while (true) { - color = it->second; - return true; + // Remove leading '#' symbol + ASSERT(styleUrl[0] == '#', (styleUrl)); + styleUrl = styleUrl.substr(1); + + auto const it = m_styleParams.find(styleUrl); + if (it != m_styleParams.cend()) + return &it->second; + + auto st = m_mapStyle2Style.find(styleUrl); + if (st != m_mapStyle2Style.end()) + styleUrl = st->second; + else + break; } - return false; -} -double KmlParser::GetTrackWidthForStyle(std::string const & styleUrl) const -{ - if (styleUrl.empty()) - return kDefaultTrackWidth; - - // Remove leading '#' symbol - auto const it = m_styleUrl2Width.find(styleUrl.substr(1)); - if (it != m_styleUrl2Width.cend()) - return it->second; - - return kDefaultTrackWidth; + return nullptr; } bool KmlParser::Push(std::string movedTag) @@ -897,9 +904,13 @@ void KmlParser::Pop(std::string_view tag) BookmarkData data; data.m_name = std::move(m_name); data.m_description = std::move(m_description); + data.m_color.m_predefinedColor = m_predefinedColor; - data.m_color.m_rgba = m_color; + // Standalone color is not defined for point placemark, but return style color with default black (as before). + data.m_color.m_rgba = m_currStyle.GetColor(0 /* defaultColor */); data.m_icon = m_icon; + data.m_iconPath = std::move(m_iconPath); + data.m_viewportScale = m_viewportScale; data.m_timestamp = m_timestamp; data.m_point = m_org; @@ -961,10 +972,8 @@ void KmlParser::Pop(std::string_view tag) { if (!m_styleId.empty()) { - m_styleUrl2Color[m_styleId] = m_color; - m_styleUrl2Width[m_styleId] = m_trackWidth; - m_color = 0; - m_trackWidth = kDefaultTrackWidth; + m_styleParams[m_styleId] = m_currStyle; + m_currStyle.Invalidate(); } } } @@ -972,20 +981,22 @@ void KmlParser::Pop(std::string_view tag) (tag == "mwm:additionalLineStyle" && m_tags.size() > 3 && GetTagFromEnd(3) == kPlacemark)) { // This code assumes that