added ability to draw texts on paths starting from the specified offsets and implemented multiple path texts layout.

This commit is contained in:
rachytski 2013-03-01 18:36:57 +03:00 committed by Alex Zolotarev
parent 2f5a78df9c
commit a1b49e2d31
10 changed files with 317 additions and 86 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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<PathTextElement> 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<Overlay> const & overlay)

View file

@ -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<Overlay> const & overlay);

View file

@ -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);
}

View file

@ -18,6 +18,7 @@ namespace graphics
size_t m_ptsCount;
double m_fullLength;
double m_pathOffset;
double m_textOffset;
Params();
};

View file

@ -120,22 +120,43 @@ namespace fwork
case feature::GEOM_LINE:
{
// typedef filter_screenpts_adapter<path_points> functor_t;
typedef filter_screenpts_adapter<cut_path_intervals> 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<cut_path_intervals> 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<path_points> 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;
}

View file

@ -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<drule::Key> 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<gp::get_path_intervals> 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<double, 16> 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<double, 16> 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;
}
}
}

View file

@ -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<di::DrawRule, 16> 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<di::DrawRule, 8> 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<double, 16> ClipIntervalsT;
ClipIntervalsT m_intervals;
typedef buffer_vector<double, 8> 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();
};
}