diff --git a/graphics/glyph_layout.cpp b/graphics/glyph_layout.cpp index a53a6d3826..f1dd7cd51e 100644 --- a/graphics/glyph_layout.cpp +++ b/graphics/glyph_layout.cpp @@ -75,7 +75,9 @@ namespace graphics m_lastVisible(visText.size()), m_fontDesc(fontDesc), m_pivot(pt), - m_offset(0, 0) + m_offset(0, 0), + m_textLength(0), + m_textOffset(0) { size_t const cnt = visText.size(); ASSERT_GREATER(cnt, 0, ()); @@ -101,6 +103,8 @@ namespace graphics GlyphMetrics const m = glyphCache->getGlyphMetrics(glyphKey); + m_textLength += m.m_xAdvance; + if (isFirst) { boundRect = m2::RectD(m.m_xOffset, @@ -157,12 +161,18 @@ namespace graphics m_lastVisible(0), m_path(src.m_path, m), m_visText(src.m_visText), - m_pos(src.m_pos), m_fontDesc(src.m_fontDesc), m_metrics(src.m_metrics), m_pivot(0, 0), - m_offset(0, 0) + m_offset(0, 0), + m_textLength(src.m_textLength) { + m_textOffset = (m2::PointD(0, src.m_textOffset) * m).Length(m2::PointD(0, 0) * m); + + /// if isReverse flag is changed, recalculate m_textOffset + if (src.m_path.isReverse() ^ m_path.isReverse()) + m_textOffset = m_path.fullLength() - m_textOffset - m_textLength; + if (!m_fontDesc.IsValid()) return; m_boundRects.push_back(m2::AnyRectD(m2::RectD(0, 0, 0, 0))); @@ -176,15 +186,16 @@ namespace graphics strings::UniString const & visText, double fullLength, double pathOffset, - graphics::EPosition pos) + double textOffset) : m_firstVisible(0), m_lastVisible(0), m_path(pts, ptsCount, fullLength, pathOffset), m_visText(visText), - m_pos(pos), m_fontDesc(fontDesc), m_pivot(0, 0), - m_offset(0, 0) + m_offset(0, 0), + m_textLength(0), + m_textOffset(textOffset) { if (!m_fontDesc.IsValid()) return; @@ -202,6 +213,7 @@ namespace graphics false, //< calculating glyph positions using the unmasked glyphs. graphics::Color(0, 0, 0, 0)); m_metrics[i] = glyphCache->getGlyphMetrics(key); + m_textLength += m_metrics[i].m_xAdvance; } recalcAlongPath(); } @@ -226,32 +238,13 @@ namespace graphics PathPoint arrPathStart = m_path.front(); - m_pivot = m_path.offsetPoint(arrPathStart, m_path.fullLength() / 2.0).m_pt; + m_pivot = m_path.offsetPoint(arrPathStart, m_textOffset).m_pt; // offset of the text from path's start - double offset = (m_path.fullLength() - strLength) / 2.0; - - if (m_pos & graphics::EPosLeft) - { - offset = 0; - m_pivot = arrPathStart.m_pt; - } - - if (m_pos & graphics::EPosRight) - { - offset = (m_path.fullLength() - strLength); - m_pivot = m_path.get(m_path.size() - 1); - } + double offset = m_textOffset; // calculate base line offset double blOffset = 2 - m_fontDesc.m_size / 2; - // on-path kerning should be done for baseline-centered glyphs - //double kernOffset = blOffset; - - if (m_pos & graphics::EPosUnder) - blOffset = 2 - m_fontDesc.m_size; - if (m_pos & graphics::EPosAbove) - blOffset = 2; offset -= m_path.pathOffset(); if (-offset >= strLength) diff --git a/graphics/glyph_layout.hpp b/graphics/glyph_layout.hpp index 87066a6fd6..aa48cc5331 100644 --- a/graphics/glyph_layout.hpp +++ b/graphics/glyph_layout.hpp @@ -41,7 +41,6 @@ namespace graphics TextPath m_path; strings::UniString m_visText; - graphics::EPosition m_pos; graphics::FontDesc m_fontDesc; @@ -52,7 +51,14 @@ namespace graphics m2::PointD m_pivot; m2::PointD m_offset; - double getKerning(GlyphLayoutElem const & prevElem, GlyphMetrics const & prevMetrics, GlyphLayoutElem const & curElem, GlyphMetrics const & curMetrics); + double m_textLength; + double m_textOffset; + + double getKerning(GlyphLayoutElem const & prevElem, + GlyphMetrics const & prevMetrics, + GlyphLayoutElem const & curElem, + GlyphMetrics const & curMetrics); + void computeBoundRects(); void recalcPivot(); @@ -78,7 +84,7 @@ namespace graphics strings::UniString const & visText, double fullLength, double pathOffset, - graphics::EPosition pos); + double textOffset); size_t firstVisible() const; size_t lastVisible() const; diff --git a/graphics/graphics_tests/screengl_test.cpp b/graphics/graphics_tests/screengl_test.cpp index 601a529e56..ee27ad2c53 100644 --- a/graphics/graphics_tests/screengl_test.cpp +++ b/graphics/graphics_tests/screengl_test.cpp @@ -948,7 +948,7 @@ namespace m_text, calc_length(m_path), 0.0, - graphics::EPosLeft, + 0, 0); } }; @@ -1093,6 +1093,7 @@ namespace params.m_glyphCache = p->glyphCache(); params.m_pivot = m_path[0]; params.m_position = graphics::EPosCenter; + params.m_textOffset = 30; graphics::PathTextElement pte(params); diff --git a/graphics/overlay_renderer.cpp b/graphics/overlay_renderer.cpp index b064e68760..67559d1dea 100644 --- a/graphics/overlay_renderer.cpp +++ b/graphics/overlay_renderer.cpp @@ -153,28 +153,31 @@ namespace graphics drawTextEx(params); } - - - bool OverlayRenderer::drawPathText( - FontDesc const & fontDesc, m2::PointD const * path, size_t s, string const & utf8Text, - double fullLength, double pathOffset, graphics::EPosition pos, double depth) + void OverlayRenderer::drawPathText(FontDesc const & fontDesc, + m2::PointD const * path, + size_t pathSize, + string const & utf8Text, + double fullLength, + double pathOffset, + double textOffset, + double depth) { if (!m_drawTexts) - return false; + return; PathTextElement::Params params; params.m_pts = path; - params.m_ptsCount = s; + params.m_ptsCount = pathSize; params.m_fullLength = fullLength; params.m_pathOffset = pathOffset; params.m_fontDesc = fontDesc; params.m_logText = strings::MakeUniString(utf8Text); params.m_depth = depth; params.m_log2vis = true; + params.m_textOffset = textOffset; params.m_glyphCache = glyphCache(); params.m_pivot = path[0]; - params.m_position = pos; shared_ptr pte(new PathTextElement(params)); @@ -184,8 +187,30 @@ namespace graphics pte->draw(this, id); else m_overlay->processOverlayElement(pte); + } - return true; + void OverlayRenderer::drawPathText(FontDesc const & fontDesc, + m2::PointD const * path, + size_t pathSize, + string const & utf8Text, + double fullLength, + double pathOffset, + double const * textOffsets, + size_t offsSize, + double depth) + { + if (!m_drawTexts) + return; + + for (unsigned i = 0; i < offsSize; ++i) + drawPathText(fontDesc, + path, + pathSize, + utf8Text, + fullLength, + pathOffset, + textOffsets[i], + depth); } void OverlayRenderer::setOverlay(shared_ptr const & overlay) diff --git a/graphics/overlay_renderer.hpp b/graphics/overlay_renderer.hpp index d2347cbe21..62f7493de5 100644 --- a/graphics/overlay_renderer.hpp +++ b/graphics/overlay_renderer.hpp @@ -65,14 +65,24 @@ namespace graphics bool log2vis, bool doSplit = false); - /// drawing text on the path - bool drawPathText(FontDesc const & fontDesc, + void drawPathText(FontDesc const & fontDesc, m2::PointD const * path, size_t s, string const & utf8Text, double fullLength, double pathOffset, - graphics::EPosition pos, + double textOffset, + double depth); + + /// drawing text on the path + void drawPathText(FontDesc const & fontDesc, + m2::PointD const * path, + size_t s, + string const & utf8Text, + double fullLength, + double pathOffset, + double const * textOffsets, + size_t offsSize, double depth); void setOverlay(shared_ptr const & overlay); diff --git a/graphics/path_text_element.cpp b/graphics/path_text_element.cpp index c5538b93a4..dc12abc9d9 100644 --- a/graphics/path_text_element.cpp +++ b/graphics/path_text_element.cpp @@ -8,7 +8,8 @@ namespace graphics : m_pts(0), m_ptsCount(0), m_fullLength(0), - m_pathOffset(0) + m_pathOffset(0), + m_textOffset(0) {} PathTextElement::PathTextElement(Params const & p) @@ -20,7 +21,7 @@ namespace graphics visText(), p.m_fullLength, p.m_pathOffset, - p.m_position) + p.m_textOffset) { setPivot(m_glyphLayout.pivot()); setIsValid((m_glyphLayout.firstVisible() == 0) && (m_glyphLayout.lastVisible() == visText().size())); @@ -44,8 +45,8 @@ namespace graphics for (unsigned i = 0; i < m_glyphLayout.boundRects().size(); ++i) m_boundRects.push_back(m_glyphLayout.boundRects()[i]); -// for (unsigned i = 0; i < m_boundRects.size(); ++i) -// m_boundRects[i] = m2::Inflate(m_boundRects[i], m2::PointD(10, 10)); + for (unsigned i = 0; i < m_boundRects.size(); ++i) + m_boundRects[i] = m2::Inflate(m_boundRects[i], m2::PointD(10, 10)); // m_boundRects[i].m2::Inflate(m2::PointD(40, 2)); //< to create more sparse street names structure setIsDirtyRect(false); } diff --git a/graphics/path_text_element.hpp b/graphics/path_text_element.hpp index d14e204a61..c165dbe55a 100644 --- a/graphics/path_text_element.hpp +++ b/graphics/path_text_element.hpp @@ -18,6 +18,7 @@ namespace graphics size_t m_ptsCount; double m_fullLength; double m_pathOffset; + double m_textOffset; Params(); }; diff --git a/map/feature_processor.cpp b/map/feature_processor.cpp index e119501fcb..fa41294189 100644 --- a/map/feature_processor.cpp +++ b/map/feature_processor.cpp @@ -120,22 +120,43 @@ namespace fwork case feature::GEOM_LINE: { -// typedef filter_screenpts_adapter functor_t; - typedef filter_screenpts_adapter functor_t; - - functor_t::params p; - - p.m_convertor = &m_convertor; - p.m_rect = &m_rect; - p.m_intervals = &fi.m_styler.m_intervals; - - functor_t fun(p); - - f.ForEachPointRef(fun, m_zoom); - if (fun.IsExist()) + if (fi.m_styler.m_hasPathText) { - isExist = true; - assign_path(fi, fun); + typedef filter_screenpts_adapter functor_t; + + functor_t::params p; + + p.m_convertor = &m_convertor; + p.m_rect = &m_rect; + p.m_intervals = &fi.m_styler.m_intervals; + + functor_t fun(p); + + f.ForEachPointRef(fun, m_zoom); + if (fun.IsExist()) + { + isExist = true; + assign_path(fi, fun); + } + } + else + { + typedef filter_screenpts_adapter functor_t; + + functor_t::params p; + + p.m_convertor = &m_convertor; + p.m_rect = &m_rect; + + functor_t fun(p); + + f.ForEachPointRef(fun, m_zoom); + + if (fun.IsExist()) + { + isExist = true; + assign_path(fi, fun); + } } break; } diff --git a/map/feature_styler.cpp b/map/feature_styler.cpp index fcc0685078..f7fd1edd89 100644 --- a/map/feature_styler.cpp +++ b/map/feature_styler.cpp @@ -1,14 +1,21 @@ #include "feature_styler.hpp" +#include "geometry_processors.hpp" +#include "proto_to_styles.hpp" #include "../indexer/drawing_rules.hpp" #include "../indexer/feature.hpp" #include "../indexer/feature_visibility.hpp" + #ifdef OMIM_PRODUCTION #include "../indexer/drules_struct_lite.pb.h" #else #include "../indexer/drules_struct.pb.h" #endif +#include "../geometry/screenbase.hpp" + +#include "../graphics/glyph_cache.hpp" + namespace { struct less_depth @@ -20,16 +27,29 @@ namespace }; } -namespace feature +namespace di { - StylesContainer::StylesContainer() {} - - StylesContainer::~StylesContainer() + uint32_t DrawRule::GetID(size_t threadSlot) const { - //for_each(m_rules.begin(), m_rules.end(), DeleteFunctor()); + return (m_transparent ? m_rule->GetID2(threadSlot) : m_rule->GetID(threadSlot)); } - void StylesContainer::GetStyles(FeatureType const & f, int const zoom) + void DrawRule::SetID(size_t threadSlot, uint32_t id) const + { + m_transparent ? m_rule->SetID2(threadSlot, id) : m_rule->SetID(threadSlot, id); + } + + FeatureStyler::FeatureStyler(FeatureType const & f, + int const zoom, + double const visualScale, + graphics::GlyphCache * glyphCache, + ScreenBase const * convertor, + m2::RectD const * rect) + : m_hasPathText(false), + m_visualScale(visualScale), + m_glyphCache(glyphCache), + m_convertor(convertor), + m_rect(rect) { vector keys; string names; // for debug use only, in release it's empty @@ -65,7 +85,8 @@ namespace feature } double area = 0.0; - if (m_geometryType != GEOM_POINT) + + if (m_geometryType != feature::GEOM_POINT) { m2::RectD const bbox = f.GetLimitRect(zoom); area = bbox.SizeX() * bbox.SizeY(); @@ -87,8 +108,12 @@ namespace feature bool hasIcon = false; bool hasCaptionWithoutOffset = false; + bool pathWasClipped = false; + m_fontSize = 0; + size_t const count = keys.size(); m_rules.resize(count); + for (size_t i = 0; i < count; ++i) { double depth = keys[i].m_priority; @@ -119,16 +144,45 @@ namespace feature m_rules[i] = di::DrawRule( drule::rules().Find(keys[i]), depth, isTransparent); - if ((m_geometryType == GEOM_LINE) && !m_hasPathText && !m_primaryText.empty()) + if ((m_geometryType == feature::GEOM_LINE) && !m_hasPathText && !m_primaryText.empty()) if (m_rules[i].m_rule->GetCaption(0) != 0) + { m_hasPathText = true; + /// calculating clip intervals only once + + if (!pathWasClipped) + { + typedef gp::filter_screenpts_adapter functor_t; + + functor_t::params p; + + p.m_convertor = m_convertor; + p.m_rect = m_rect; + p.m_intervals = &m_intervals; + + functor_t fun(p); + + f.ForEachPointRef(fun, zoom); + + m_pathLength = fun.m_length; + pathWasClipped = true; + } + + if (!FilterTextSize(m_rules[i].m_rule)) + m_fontSize = max(m_fontSize, GetTextFontSize(m_rules[i].m_rule)); + } + if (keys[i].m_type == drule::caption) if (m_rules[i].m_rule->GetCaption(0) != 0) if (!m_rules[i].m_rule->GetCaption(0)->has_offset_y()) hasCaptionWithoutOffset = true; } + /// placing a text on the path + if (m_hasPathText && (m_fontSize != 0)) + LayoutTexts(); + if (hasIcon && hasCaptionWithoutOffset) // we need to delete symbol style (single one due to MakeUnique call above) for (size_t i = 0; i < count; ++i) @@ -143,4 +197,80 @@ namespace feature sort(m_rules.begin(), m_rules.end(), less_depth()); } + + void FeatureStyler::LayoutTexts() + { + m_textLength = m_glyphCache->getTextLength(m_fontSize, GetPathName()); + double emptySize = max(200 * m_visualScale, m_textLength); + double minPeriodSize = emptySize + m_textLength; + + size_t textCnt = 0; + double firstTextOffset = 0; + + if (m_pathLength > m_textLength) + { + textCnt = ceil((m_pathLength - m_textLength) / minPeriodSize); + firstTextOffset = 0.5 * (m_pathLength - (textCnt * m_textLength + (textCnt - 1) * emptySize)); + } + + if ((textCnt != 0) && (!m_intervals.empty())) + { + buffer_vector deadZones; + + for (unsigned i = 0; i < textCnt; ++i) + { + double deadZoneStart = firstTextOffset + (m_textLength + minPeriodSize) * i; + double deadZoneEnd = deadZoneStart + m_textLength; + + if (deadZoneStart > m_intervals.back()) + break; + + deadZones.push_back(deadZoneStart); + deadZones.push_back(deadZoneEnd); + m_offsets.push_back(deadZoneStart); + } + + if (!deadZones.empty()) + { + buffer_vector res(deadZones.size() + m_intervals.size()); + + int k = my::MergeSorted(&m_intervals[0], m_intervals.size(), + &deadZones[0], deadZones.size(), + &res[0], res.size()); + + res.resize(k); + + m_intervals = res; + } + } + } + + string const FeatureStyler::GetPathName() const + { + if (m_secondaryText.empty()) + return m_primaryText; + else + return m_primaryText + " " + m_secondaryText; + } + + bool FeatureStyler::IsEmpty() const + { + return m_rules.empty(); + } + + uint8_t FeatureStyler::GetTextFontSize(drule::BaseRule const * pRule) const + { + return pRule->GetCaption(0)->height() * m_visualScale; + } + + bool FeatureStyler::FilterTextSize(drule::BaseRule const * pRule) const + { + if (pRule->GetCaption(0)) + return (GetFontSize(pRule->GetCaption(0)) < 3); + else + { + // this rule is not a caption at all + return true; + } + } } diff --git a/map/feature_styler.hpp b/map/feature_styler.hpp index de52ffe585..47e54da20b 100644 --- a/map/feature_styler.hpp +++ b/map/feature_styler.hpp @@ -1,21 +1,51 @@ #pragma once -#include "drawer.hpp" +#include "../indexer/drawing_rules.hpp" + +#include "../geometry/rect2d.hpp" #include "../std/vector.hpp" class FeatureType; -namespace feature +namespace graphics { - class StylesContainer + class GlyphCache; +} + +class ScreenBase; + +namespace di +{ + struct DrawRule { + drule::BaseRule const * m_rule; + double m_depth; + bool m_transparent; - public: - StylesContainer(); - ~StylesContainer(); + DrawRule() : m_rule(0) {} + DrawRule(drule::BaseRule const * p, + double d, + bool tr) + : m_rule(p), + m_depth(d), + m_transparent(tr) + {} - typedef buffer_vector StylesContainerT; + uint32_t GetID(size_t threadSlot) const; + void SetID(size_t threadSlot, uint32_t id) const; + }; + + struct FeatureStyler + { + FeatureStyler(FeatureType const & f, + int const zoom, + double const visualScale, + graphics::GlyphCache * glyphCache, + ScreenBase const * convertor, + m2::RectD const * rect); + + typedef buffer_vector StylesContainerT; StylesContainerT m_rules; bool m_isCoastline; @@ -23,21 +53,34 @@ namespace feature bool m_hasPathText; int m_geometryType; + double m_visualScale; + string m_primaryText; string m_secondaryText; string m_refText; + typedef buffer_vector ClipIntervalsT; + ClipIntervalsT m_intervals; + + typedef buffer_vector TextOffsetsT; + TextOffsetsT m_offsets; + + double m_pathLength; + double m_textLength; + uint8_t m_fontSize; + + graphics::GlyphCache * m_glyphCache; + ScreenBase const * m_convertor; + m2::RectD const * m_rect; + double m_popRank; - void GetStyles(FeatureType const & f, int const zoom); + bool IsEmpty() const; - inline bool IsEmpty() const - { - return m_rules.empty(); - } - inline size_t GetCount() const - { - return m_rules.size(); - } + string const GetPathName() const; + + bool FilterTextSize(drule::BaseRule const * pRule) const; + uint8_t GetTextFontSize(drule::BaseRule const * pRule) const; + void LayoutTexts(); }; }