diff --git a/map/framework.cpp b/map/framework.cpp index 664f014f1d..0a144ab2d9 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -236,7 +236,7 @@ namespace fwork if (fontSize != 0) { double textLength = m_glyphCache->getTextLength(fontSize, ptr->m_name); - typedef calc_length functor_t; + typedef calc_length functor_t; functor_t::params p1; p1.m_convertor = &m_convertor; p1.m_rect = &m_rect; diff --git a/std/sstream.hpp b/std/sstream.hpp index 2280a4c9b9..dd6476b512 100644 --- a/std/sstream.hpp +++ b/std/sstream.hpp @@ -9,6 +9,7 @@ using std::istringstream; using std::ostringstream; +using std::stringstream; using std::endl; #ifdef DEBUG_NEW diff --git a/yg/glyph_layout.cpp b/yg/glyph_layout.cpp index 61fc11b56d..fd9766973f 100644 --- a/yg/glyph_layout.cpp +++ b/yg/glyph_layout.cpp @@ -2,10 +2,14 @@ #include "glyph_layout.hpp" #include "resource_manager.hpp" +#include "skin.hpp" #include "font_desc.hpp" +#include "resource_style.hpp" +#include "text_path.hpp" #include "../base/logging.hpp" #include "../base/math.hpp" +#include "../std/sstream.hpp" #include "../geometry/angles.hpp" #include "../geometry/aa_rect2d.hpp" @@ -27,10 +31,8 @@ namespace yg { double m_angle; PathPoint m_pp; - - /// @todo Need to initialize or not ??? - //PivotPoint(double angle = 0, PathPoint const & pp = PathPoint()) - //{} + PivotPoint(double angle = 0, PathPoint const & pp = PathPoint()) + {} }; class pts_array @@ -187,6 +189,98 @@ namespace yg return res; } + GlyphLayout::GlyphLayout(shared_ptr const & resourceManager, + shared_ptr const & skin, + FontDesc const & fontDesc, + m2::PointD const & pt, + wstring const & text, + yg::EPosition pos) + : m_resourceManager(resourceManager), + m_firstVisible(0), + m_lastVisible(text.size()) + { + m2::PointD pv = pt; + + for (size_t i = 0; i < text.size(); ++i) + { + GlyphKey glyphKey(text[i], fontDesc.m_size, fontDesc.m_isMasked, fontDesc.m_color); + + if (fontDesc.m_isStatic) + { + uint32_t glyphID = skin->mapGlyph(glyphKey, fontDesc.m_isStatic); + CharStyle const * p = static_cast(skin->fromID(glyphID)); + if (p != 0) + { + if (i == 0) + m_limitRect = m2::RectD(p->m_xOffset + pv.x, + p->m_yOffset + pv.y, + p->m_xOffset + pv.x, + p->m_yOffset + pv.y); + else + m_limitRect.Add(m2::PointD(p->m_xOffset, p->m_yOffset) + pv); + + m_limitRect.Add(m2::PointD(p->m_xOffset + p->m_texRect.SizeX() - 4, + p->m_yOffset + p->m_texRect.SizeY() - 4) + pv); + + } + + GlyphLayoutElem elem; + + elem.m_sym = text[i]; + elem.m_angle = 0; + elem.m_pt = pv; + elem.m_metrics.m_height = p->m_texRect.SizeY() - 4; + elem.m_metrics.m_width = p->m_texRect.SizeX() - 4; + elem.m_metrics.m_xAdvance = p->m_xAdvance; + elem.m_metrics.m_xOffset = p->m_xOffset; + elem.m_metrics.m_yOffset = p->m_yOffset; + elem.m_metrics.m_yAdvance = 0; + + m_entries.push_back(elem); + + pv += m2::PointD(p->m_xAdvance, 0); + } + else + { + GlyphMetrics const m = resourceManager->getGlyphMetrics(glyphKey); + if (i == 0) + m_limitRect = m2::RectD(m.m_xOffset + pv.x, m.m_yOffset + pv.y, m.m_xOffset + pv.x, m.m_yOffset + pv.y); + else + m_limitRect.Add(m2::PointD(m.m_xOffset, m.m_yOffset) + pv); + + m_limitRect.Add(m2::PointD(m.m_xOffset + m.m_xAdvance, + m.m_yOffset + m.m_yAdvance) + pv); + + GlyphLayoutElem elem; + elem.m_sym = text[i]; + elem.m_angle = 0; + elem.m_pt = pv; + elem.m_metrics = m; + m_entries.push_back(elem); + + pv += m2::PointD(m.m_xAdvance, m.m_yAdvance); + } + } + + m2::PointD ptOffs(-m_limitRect.SizeX() / 2, + -m_limitRect.SizeY() / 2); + + /// adjusting according to position + if (pos & EPosLeft) + ptOffs += m2::PointD(-m_limitRect.SizeX() / 2, 0); + if (pos & EPosRight) + ptOffs += m2::PointD(m_limitRect.SizeX() / 2, 0); + + if (pos & EPosAbove) + ptOffs += m2::PointD(0, m_limitRect.SizeY() / 2); + + if (pos & EPosUnder) + ptOffs += m2::PointD(0, -m_limitRect.SizeY() / 2); + + offset(ptOffs); + } + + GlyphLayout::GlyphLayout(shared_ptr const & resourceManager, FontDesc const & fontDesc, m2::PointD const * pts, @@ -199,7 +293,7 @@ namespace yg m_firstVisible(0), m_lastVisible(0) { - pts_array arrPath(pts, ptsCount, fullLength, pathOffset); + TextPath arrPath(pts, ptsCount, fullLength, pathOffset); // get vector of glyphs and calculate string length double strLength = 0.0; @@ -311,6 +405,31 @@ namespace yg m_lastVisible = symPos + 1; } + + bool isFirst = true; + + for (unsigned i = m_firstVisible; i < m_lastVisible; ++i) + { + m2::AARectD symRectAA( + m_entries[i].m_pt.Move(m_entries[i].m_metrics.m_height, m_entries[i].m_angle - math::pi / 2), + m_entries[i].m_angle, + m2::RectD(m_entries[i].m_metrics.m_xOffset, + m_entries[i].m_metrics.m_yOffset, + m_entries[i].m_metrics.m_xOffset + m_entries[i].m_metrics.m_width, + m_entries[i].m_metrics.m_yOffset + m_entries[i].m_metrics.m_height)); + + m2::PointD pts[4]; + symRectAA.GetGlobalPoints(pts); + + if (isFirst) + m_limitRect = m2::RectD(pts[0].x, pts[0].y, pts[0].x, pts[0].y); + else + m_limitRect.Add(pts[0]); + + m_limitRect.Add(pts[1]); + m_limitRect.Add(pts[2]); + m_limitRect.Add(pts[3]); + } } size_t GlyphLayout::firstVisible() const @@ -330,32 +449,13 @@ namespace yg m2::RectD const GlyphLayout::limitRect() const { - bool isFirst = true; - m2::RectD res; + return m_limitRect; + } - for (unsigned i = m_firstVisible; i < m_lastVisible; ++i) - { - m2::AARectD symRectAA( - m_entries[i].m_pt.Move(m_entries[i].m_metrics.m_height, m_entries[i].m_angle - math::pi / 2), - m_entries[i].m_angle, - m2::RectD(m_entries[i].m_metrics.m_xOffset, - m_entries[i].m_metrics.m_yOffset, - m_entries[i].m_metrics.m_xOffset + m_entries[i].m_metrics.m_width, - m_entries[i].m_metrics.m_yOffset + m_entries[i].m_metrics.m_height)); - - m2::PointD pts[4]; - symRectAA.GetGlobalPoints(pts); - - if (isFirst) - res = m2::RectD(pts[0].x, pts[0].y, pts[0].x, pts[0].y); - else - res.Add(pts[0]); - - res.Add(pts[1]); - res.Add(pts[2]); - res.Add(pts[3]); - } - - return res; + void GlyphLayout::offset(m2::PointD const & offs) + { + for (unsigned i = 0; i < m_entries.size(); ++i) + m_entries[i].m_pt += offs; + m_limitRect.Offset(offs); } } diff --git a/yg/glyph_layout.hpp b/yg/glyph_layout.hpp index 342631e8e8..83e7e1dfc2 100644 --- a/yg/glyph_layout.hpp +++ b/yg/glyph_layout.hpp @@ -2,6 +2,7 @@ #include "defines.hpp" #include "../geometry/rect2d.hpp" +#include "../geometry/point2d.hpp" #include "../std/vector.hpp" #include "../std/string.hpp" @@ -14,6 +15,7 @@ namespace yg { class ResourceManager; + class Skin; struct FontDesc; struct GlyphLayoutElem @@ -35,12 +37,21 @@ namespace yg vector m_entries; + m2::RectD m_limitRect; + double getKerning(GlyphLayoutElem const & prevElem, GlyphLayoutElem const & curElem); public: GlyphLayout(GlyphLayout const & layout, double shift); + GlyphLayout(shared_ptr const & resourceManager, + shared_ptr const & skin, + FontDesc const & font, + m2::PointD const & pt, + wstring const & text, + yg::EPosition pos); + GlyphLayout(shared_ptr const & resourceManager, FontDesc const & font, m2::PointD const * pts, @@ -56,5 +67,7 @@ namespace yg vector const & entries() const; m2::RectD const limitRect() const; + + void offset(m2::PointD const & offs); }; } diff --git a/yg/layout_element.cpp b/yg/layout_element.cpp deleted file mode 100644 index 4a5ae3dfc4..0000000000 --- a/yg/layout_element.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "../base/SRC_FIRST.hpp" - -#include "layout_element.hpp" -#include "screen.hpp" - -namespace yg -{ - LayoutElement::LayoutElement(int groupID, m2::PointD const & pivot, EPosition pos) - : m_groupID(groupID), m_pivot(pivot), m_pos(pos) - {} - - int LayoutElement::groupID() const - { - return m_groupID; - } - - m2::PointD const & LayoutElement::pivot() const - { - return m_pivot; - } - - EPosition LayoutElement::position() const - { - return m_pos; - } - - bool LayoutElement::isFreeElement() const - { - return m_isFreeElement; - } - - void LayoutElement::setIsFreeElement(bool flag) const - { - m_isFreeElement = flag; - } - - bool LayoutElement::isFrozen() const - { - return m_isFrozen; - } - - void LayoutElement::setIsFrozen(bool flag) const - { - m_isFrozen = flag; - } - - bool LayoutElement::doNeedRedraw() const - { - return m_doNeedRedraw; - } - - void LayoutElement::setNeedRedraw(bool flag) const - { - m_doNeedRedraw = flag; - } -} diff --git a/yg/layout_element.hpp b/yg/layout_element.hpp deleted file mode 100644 index c52520a376..0000000000 --- a/yg/layout_element.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "../geometry/point2d.hpp" -#include "../geometry/rect2d.hpp" -#include "defines.hpp" - -namespace yg -{ - class Screen; - struct FontDesc; - - class LayoutElement - { - private: - - int m_groupID; - m2::PointD m_pivot; - EPosition m_pos; - - mutable bool m_isFreeElement; - mutable bool m_isFrozen; - mutable bool m_doNeedRedraw; - - public: - LayoutElement(int groupID, m2::PointD const & pivot, EPosition pos); - /// id of the group, composed of several layoutElements - int groupID() const; - /// pivot is expressed in group coordinates - m2::PointD const & pivot() const; - /// position of the element related to pivot point - EPosition position() const; - - bool isFreeElement() const; - void setIsFreeElement(bool flag) const; - - bool isFrozen() const; - void setIsFrozen(bool flag) const; - - bool doNeedRedraw() const; - void setNeedRedraw(bool flag) const; - - /// bounding rect in pivot-aligned coordinates - virtual m2::RectD const boundRect() const = 0; - /// draw layout element - virtual void draw(Screen * screen) = 0; - }; -} diff --git a/yg/symbol_layout_element.hpp b/yg/symbol_layout_element.hpp deleted file mode 100644 index 6dea0d41e2..0000000000 --- a/yg/symbol_layout_element.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "layout_element.hpp" - -namespace yg -{ - class SymbolLayoutElement : public LayoutElement - { - public: - SymbolLayoutElement(); - - m2::RectD const boundRect() const; - - void draw(Screen * screen); - }; -} diff --git a/yg/text_element.cpp b/yg/text_element.cpp new file mode 100644 index 0000000000..498ce1f1a3 --- /dev/null +++ b/yg/text_element.cpp @@ -0,0 +1,134 @@ +#include "../base/SRC_FIRST.hpp" + +#include "text_element.hpp" +#include "screen.hpp" +#include "skin.hpp" +#include "resource_style.hpp" + +#include "../base/string_utils.hpp" + +namespace yg +{ + OverlayElement::OverlayElement(Params const & p) + : m_pivot(p.m_pivot), m_position(p.m_position) + {} + + void OverlayElement::offset(m2::PointD const & offs) + { + m_pivot += offs; + } + + m2::PointD const & OverlayElement::pivot() const + { + return m_pivot; + } + + void OverlayElement::setPivot(m2::PointD const & pivot) + { + m_pivot = pivot; + } + + yg::EPosition OverlayElement::position() const + { + return m_position; + } + + void OverlayElement::setPosition(yg::EPosition pos) + { + m_position = pos; + } + + TextElement::TextElement(Params const & p) + : OverlayElement(p), + m_fontDesc(p.m_fontDesc), + m_utf8Text(p.m_utf8Text), + m_depth(p.m_depth), + m_log2vis(p.m_log2vis), + m_rm(p.m_rm), + m_skin(p.m_skin) + { + } + + void TextElement::drawTextImpl(GlyphLayout const & layout, gl::Screen * screen, FontDesc const & fontDesc) + { + for (unsigned i = layout.firstVisible(); i < layout.lastVisible(); ++i) + { + shared_ptr skin = screen->skin(); + GlyphLayoutElem elem = layout.entries()[i]; + uint32_t const glyphID = skin->mapGlyph(GlyphKey(elem.m_sym, fontDesc.m_size, fontDesc.m_isMasked, fontDesc.m_isMasked ? fontDesc.m_maskColor : fontDesc.m_color), fontDesc.m_isStatic); + CharStyle const * charStyle = static_cast(skin->fromID(glyphID)); + + screen->drawGlyph(elem.m_pt, m2::PointD(0.0, 0.0), elem.m_angle, 0, charStyle, m_depth); + } + } + + StraightTextElement::StraightTextElement(Params const & p) + : TextElement(p), + m_glyphLayout(p.m_rm, + p.m_skin, + p.m_fontDesc, + p.m_pivot, + strings::FromUtf8(p.m_utf8Text), + p.m_position) + { + } + + m2::RectD const StraightTextElement::boundRect() const + { + return m_glyphLayout.limitRect(); + } + + void StraightTextElement::draw(gl::Screen * screen) + { + yg::FontDesc desc = m_fontDesc; + if (m_fontDesc.m_isMasked) + { + drawTextImpl(m_glyphLayout, screen, m_fontDesc); + desc.m_isMasked = false; + } + + drawTextImpl(m_glyphLayout, screen, desc); + } + + void StraightTextElement::offset(m2::PointD const & offs) + { + TextElement::offset(offs); + m_glyphLayout.offset(offs); + } + + PathTextElement::PathTextElement(Params const & p) + : TextElement(p), + m_glyphLayout(p.m_rm, + p.m_fontDesc, + p.m_pts, + p.m_ptsCount, + strings::FromUtf8(p.m_utf8Text), + p.m_fullLength, + p.m_pathOffset, + p.m_position) + { + } + + m2::RectD const PathTextElement::boundRect() const + { + return m_glyphLayout.limitRect(); + } + + void PathTextElement::draw(gl::Screen * screen) + { + yg::FontDesc desc = m_fontDesc; + if (m_fontDesc.m_isMasked) + { + drawTextImpl(m_glyphLayout, screen, m_fontDesc); + desc.m_isMasked = false; + } + + drawTextImpl(m_glyphLayout, screen, desc); + } + + void PathTextElement::offset(m2::PointD const & offs) + { + TextElement::offset(offs); + m_glyphLayout.offset(offs); + } +} diff --git a/yg/text_element.hpp b/yg/text_element.hpp new file mode 100644 index 0000000000..93097850a7 --- /dev/null +++ b/yg/text_element.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "../geometry/point2d.hpp" +#include "../geometry/rect2d.hpp" + +#include "../std/shared_ptr.hpp" + +#include "defines.hpp" +#include "font_desc.hpp" +#include "glyph_layout.hpp" + +namespace yg +{ + class ResourceManager; + class Skin; + + namespace gl + { + class Screen; + } + + class OverlayElement + { + private: + + m2::PointD m_pivot; + yg::EPosition m_position; + + public: + + struct Params + { + m2::PointD m_pivot; + yg::EPosition m_position; + }; + + OverlayElement(Params const & p); + + virtual void offset(m2::PointD const & offs) = 0; + virtual m2::RectD const boundRect() const = 0; + virtual void draw(gl::Screen * screen) = 0; + + m2::PointD const & pivot() const; + void setPivot(m2::PointD const & pv); + + yg::EPosition position() const; + void setPosition(yg::EPosition pos); + }; + + class TextElement : public OverlayElement + { + protected: + + /// text-element specific + FontDesc m_fontDesc; + string m_utf8Text; + double m_depth; + bool m_log2vis; + shared_ptr m_rm; + shared_ptr m_skin; + + public: + + struct Params : OverlayElement::Params + { + FontDesc m_fontDesc; + string m_utf8Text; + double m_depth; + bool m_log2vis; + shared_ptr m_rm; + shared_ptr m_skin; + }; + + TextElement(Params const & p); + + void drawTextImpl(GlyphLayout const & layout, gl::Screen * screen, FontDesc const & desc); + }; + + class StraightTextElement : public TextElement + { + private: + + GlyphLayout m_glyphLayout; + + public: + + struct Params : TextElement::Params + {}; + + StraightTextElement(Params const & p); + + m2::RectD const boundRect() const; + void draw(gl::Screen * screen); + void offset(m2::PointD const & offs); + }; + + class PathTextElement : public TextElement + { + private: + + GlyphLayout m_glyphLayout; + + public: + struct Params : TextElement::Params + { + m2::PointD const * m_pts; + size_t m_ptsCount; + double m_fullLength; + double m_pathOffset; + }; + + PathTextElement(Params const & p); + + m2::RectD const boundRect() const; + void draw(gl::Screen * screen); + void offset(m2::PointD const & offs); + }; +} diff --git a/yg/text_layout_element.cpp b/yg/text_layout_element.cpp deleted file mode 100644 index 1a774f42fe..0000000000 --- a/yg/text_layout_element.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "../base/SRC_FIRST.hpp" -#include "../base/string_utils.hpp" - -#include "text_layout_element.hpp" -#include "glyph_cache.hpp" -#include "screen.hpp" -#include "resource_manager.hpp" -#include "resource_style.hpp" -#include "skin.hpp" - -namespace yg -{ - TextLayoutElement::TextLayoutElement( - char const * text, - double depth, - FontDesc const & fontDesc, - bool log2vis, - shared_ptr const & skin, - shared_ptr const & rm, - m2::PointD const & pivot, - yg::EPosition pos) - : LayoutElement(0, pivot, pos), - m_text(strings::FromUtf8(text)), - m_depth(depth), - m_fontDesc(fontDesc), - m_skin(skin), - m_rm(rm), - m_log2vis(log2vis) - { - for (size_t i = 0; i < m_text.size(); ++i) - { - GlyphKey glyphKey(m_text[i], m_fontDesc.m_size, m_fontDesc.m_isMasked, m_fontDesc.m_color); - - if (m_fontDesc.m_isStatic) - { - uint32_t glyphID = m_skin->mapGlyph(glyphKey, m_fontDesc.m_isStatic); - CharStyle const * p = static_cast(m_skin->fromID(glyphID)); - if (p != 0) - { - if (i == 0) - m_limitRect = m2::RectD(p->m_xOffset, p->m_yOffset, p->m_xOffset, p->m_yOffset); - else - m_limitRect.Add(m2::PointD(p->m_xOffset, p->m_yOffset)); - - m_limitRect.Add(m2::PointD(p->m_xOffset + p->m_texRect.SizeX() - 4, - p->m_yOffset + p->m_texRect.SizeY() - 4)); - - } - } - else - { - GlyphMetrics const m = m_rm->getGlyphMetrics(glyphKey); - if (i == 0) - m_limitRect = m2::RectD(m.m_xOffset, m.m_yOffset, m.m_xOffset, m.m_yOffset); - else - m_limitRect.Add(m2::PointD(m.m_xOffset, m.m_yOffset)); - - m_limitRect.Add(m2::PointD(m.m_xOffset + m.m_xAdvance, - m.m_yOffset + m.m_yAdvance)); - } - } - - /// centered by default - m_limitRect.Offset(-m_limitRect.SizeX() / 2, - -m_limitRect.SizeY() / 2); - - /// adjusting according to position - if (position() & EPosLeft) - m_limitRect.Offset(-m_limitRect.SizeX() / 2, 0); - if (position() & EPosRight) - m_limitRect.Offset(m_limitRect.SizeX() / 2, 0); - - if (position() & EPosAbove) - m_limitRect.Offset(0, m_limitRect.SizeY() / 2); - - if (position() & EPosUnder) - m_limitRect.Offset(0, -m_limitRect.SizeY() / 2); - } - - m2::RectD const TextLayoutElement::boundRect() const - { - return m_limitRect; - } - - void TextLayoutElement::draw(Screen * /*screen*/) - { - /* - yg::FontDesc desc = m_fontDesc; - if (desc.m_isMasked) - { - desc.m_isMasked = false; - } - */ - } -} diff --git a/yg/text_layout_element.hpp b/yg/text_layout_element.hpp deleted file mode 100644 index 285c603514..0000000000 --- a/yg/text_layout_element.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "layout_element.hpp" -#include "skin.hpp" -#include "font_desc.hpp" - -#include "../std/shared_ptr.hpp" - -namespace yg -{ - class Skin; - class ResourceManager; - - class TextLayoutElement : public LayoutElement - { - private: - - wstring m_text; - double m_depth; - FontDesc m_fontDesc; - shared_ptr m_skin; - shared_ptr m_rm; - bool m_log2vis; - m2::RectD m_limitRect; - - public: - - TextLayoutElement( - char const * text, - double depth, - FontDesc const & fontDesc, - bool log2vis, - shared_ptr const & skin, - shared_ptr const & rm, - m2::PointD const & pivot, - yg::EPosition pos); - - m2::RectD const boundRect() const; - - void draw(Screen * screen); - }; -} diff --git a/yg/text_on_path_layout_element.hpp b/yg/text_on_path_layout_element.hpp deleted file mode 100644 index 237d7aeaa4..0000000000 --- a/yg/text_on_path_layout_element.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "text_layout_element.hpp" -#include "../std/shared_ptr.hpp" - -namespace yg -{ - struct FontDesc; - class TextPath; - - class TextOnPathLayoutElement : public TextLayoutElement - { - public: - TextOnPathLayoutElement(shared_ptr const & path, - char const * text, - double depth, - FontDesc const & fontDesc); - - m2::RectD const boundRect() const; - - void draw(Screen * screen); - }; -} diff --git a/yg/text_path.cpp b/yg/text_path.cpp new file mode 100644 index 0000000000..74bf178fb8 --- /dev/null +++ b/yg/text_path.cpp @@ -0,0 +1,122 @@ +#include "../base/SRC_FIRST.hpp" + +#include "text_path.hpp" + +#include "../geometry/angles.hpp" + +namespace yg +{ + PathPoint::PathPoint(int i, m2::PointD const & pt) + : m_i(i), + m_pt(pt) + {} + + PivotPoint::PivotPoint(double angle, PathPoint const & pp) + : m_angle(angle), m_pp(pp) + {} + TextPath::TextPath(m2::PointD const * arr, size_t sz, double fullLength, double & pathOffset) + : m_arr(arr), m_size(sz), m_reverse(false) + { + ASSERT ( m_size > 1, () ); + + /* assume, that readable text in path should be ('o' - start draw point): + * / o + * / \ + * / or \ + * o \ + */ + + double const a = ang::AngleTo(m_arr[0], m_arr[m_size-1]); + if (fabs(a) > math::pi / 2.0) + { + // if we swap direction, we need to recalculate path offset from the end + double len = 0.0; + for (size_t i = 1; i < m_size; ++i) + len += m_arr[i-1].Length(m_arr[i]); + + pathOffset = fullLength - pathOffset - len; + ASSERT ( pathOffset >= -1.0E-6, () ); + if (pathOffset < 0.0) pathOffset = 0.0; + + m_reverse = true; + } + } + + size_t TextPath::size() const { return m_size; } + + m2::PointD TextPath::get(size_t i) const + { + ASSERT ( i < m_size, ("Index out of range") ); + return m_arr[m_reverse ? m_size - i - 1 : i]; + } + + m2::PointD TextPath::operator[](size_t i) const { return get(i); } + + PathPoint const TextPath::offsetPoint(PathPoint const & pp, double offset) + { + PathPoint res = pp; + + if (res.m_i == -1) + return res; + + for (size_t i = res.m_i; i < size() - 1; ++i) + { + double l = res.m_pt.Length(get(i + 1)); + res.m_pt = res.m_pt.Move(min(l, offset), ang::AngleTo(get(i), get(i + 1))); + res.m_i = i; + + if (offset <= l) + break; + else + offset -= l; + } + + return res; + + } + + PivotPoint TextPath::findPivotPoint(PathPoint const & pp, GlyphMetrics const & sym, double kern) + { + PathPoint startPt = offsetPoint(pp, kern); + + PivotPoint res; + res.m_pp.m_i = -1; + m2::PointD pt1 = startPt.m_pt; + + double angle = 0; + double advance = sym.m_xOffset + sym.m_width / 2.0; + + int j = startPt.m_i; + + while (advance > 0) + { + if (j + 1 == size()) + return res; + + double l = get(j + 1).Length(pt1); + + angle += ang::AngleTo(get(j), get(j + 1)); + + if (l < advance) + { + advance -= l; + pt1 = get(j + 1); + ++j; + } + else + { + res.m_pp.m_i = j; + res.m_pp.m_pt = pt1.Move(advance, ang::AngleTo(get(j), get(j + 1))); + advance = 0; + + angle /= (res.m_pp.m_i - startPt.m_i + 1); + res.m_angle = angle; + + break; + } + } + + return res; + } +} + diff --git a/yg/text_path.hpp b/yg/text_path.hpp new file mode 100644 index 0000000000..64362a96ef --- /dev/null +++ b/yg/text_path.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "../geometry/point2d.hpp" +#include "glyph_cache.hpp" + +namespace yg +{ + struct PathPoint + { + int m_i; + m2::PointD m_pt; + PathPoint(int i = -1, + m2::PointD const & pt = m2::PointD()); + }; + + struct PivotPoint + { + double m_angle; + PathPoint m_pp; + PivotPoint(double angle = 0, PathPoint const & pp = PathPoint()); + }; + + class TextPath + { + m2::PointD const * m_arr; + size_t m_size; + bool m_reverse; + public: + TextPath(m2::PointD const * arr, size_t sz, double fullLength, double & pathOffset); + + size_t size() const; + + m2::PointD get(size_t i) const; + m2::PointD operator[](size_t i) const; + + PathPoint const offsetPoint(PathPoint const & pp, double offset); + + PivotPoint findPivotPoint(PathPoint const & pp, GlyphMetrics const & sym, double kern); + }; +} diff --git a/yg/yg.pro b/yg/yg.pro index 386f00a95b..c479aa6532 100644 --- a/yg/yg.pro +++ b/yg/yg.pro @@ -55,10 +55,10 @@ SOURCES += \ symbol_renderer.cpp \ circle_info.cpp \ area_renderer.cpp \ - layout_element.cpp \ font_desc.cpp \ glyph_layout.cpp \ - text_layout_element.cpp + text_element.cpp \ + text_path.cpp HEADERS += \ internal/opengl.hpp \ @@ -106,12 +106,10 @@ HEADERS += \ symbol_renderer.hpp \ circle_info.hpp \ area_renderer.hpp \ - layout_element.hpp \ font_desc.hpp \ glyph_layout.hpp \ - text_on_path_layout_element.hpp \ - text_layout_element.hpp \ - symbol_layout_element.hpp + text_element.hpp \ + text_path.hpp win32 { HEADERS += internal/opengl_win32.hpp diff --git a/yg/yg_tests/screengl_test.cpp b/yg/yg_tests/screengl_test.cpp index 208ad41e00..c3afa540a8 100644 --- a/yg/yg_tests/screengl_test.cpp +++ b/yg/yg_tests/screengl_test.cpp @@ -7,6 +7,7 @@ #include "../../yg/skin.hpp" #include "../../yg/pen_info.hpp" #include "../../yg/circle_info.hpp" +#include "../../yg/text_element.hpp" #include "../../qt_tstfrm/macros.hpp" @@ -832,6 +833,81 @@ namespace } }; + struct TestDrawStraightTextElement + { + yg::PenInfo m_penInfo; + vector m_path; + TestDrawStraightTextElement() + { + m_path.push_back(m2::PointD(100, 200)); + m_path.push_back(m2::PointD(500, 200)); + double pat[] = { 2, 2 }; + m_penInfo = yg::PenInfo(yg::Color(0, 0, 0, 0xFF), 2, &pat[0], ARRAY_SIZE(pat), 0); + } + + void DoDraw(shared_ptr p) + { + yg::StraightTextElement::Params params; + params.m_fontDesc = yg::FontDesc(false, 20); + params.m_utf8Text = "Simplicity is the ultimate sophistication. Leonardo Da Vinci."; + params.m_depth = 10; + params.m_log2vis = false; + params.m_rm = p->resourceManager(); + params.m_skin = p->skin(); + params.m_pivot = m_path[0]; + params.m_position = yg::EPosRight; + + yg::StraightTextElement ste(params); + + p->drawPath(&m_path[0], m_path.size(), 0, p->skin()->mapPenInfo(m_penInfo), 0); + ste.draw(p.get()); + } + }; + + struct TestDrawPathTextElement + { + vector m_path; + yg::PenInfo m_penInfo; + + TestDrawPathTextElement() + { + m_path.push_back(m2::PointD(40, 200)); + m_path.push_back(m2::PointD(100, 100)); + m_path.push_back(m2::PointD(160, 200)); + m_path.push_back(m2::PointD(200, 100)); + m_path.push_back(m2::PointD(240, 200)); + m_path.push_back(m2::PointD(280, 100)); + m_path.push_back(m2::PointD(320, 200)); + m_path.push_back(m2::PointD(360, 100)); + m_path.push_back(m2::PointD(400, 200)); + + double pat[] = { 2, 2 }; + m_penInfo = yg::PenInfo(yg::Color(0, 0, 0, 0xFF), 2, &pat[0], ARRAY_SIZE(pat), 0); + } + + void DoDraw(shared_ptr p) + { + yg::PathTextElement::Params params; + params.m_pts = &m_path[0]; + params.m_ptsCount = m_path.size(); + params.m_fullLength = calc_length(m_path); + params.m_pathOffset = 0; + params.m_fontDesc = yg::FontDesc(false, 20); + params.m_utf8Text = "Simplicity is the ultimate sophistication. Leonardo Da Vinci."; + params.m_depth = 10; + params.m_log2vis = false; + params.m_rm = p->resourceManager(); + params.m_skin = p->skin(); + params.m_pivot = m_path[0]; + params.m_position = yg::EPosCenter; + + yg::PathTextElement pte(params); + + p->drawPath(&m_path[0], m_path.size(), 0, p->skin()->mapPenInfo(m_penInfo), 0); + pte.draw(p.get()); + } + }; + struct TestDrawTextOnPathZigZag { std::vector m_path; @@ -1166,11 +1242,13 @@ namespace // UNIT_TEST_GL(TestDrawUnicodeSymbols); // UNIT_TEST_GL(TestDrawTextRectWithFixedFont); // UNIT_TEST_GL(TestDrawStringOnString); - UNIT_TEST_GL(TestDrawTextOnPathInteractive); - UNIT_TEST_GL(TestDrawTextOnPathBigSymbols); - UNIT_TEST_GL(TestDrawTextOnPath); - UNIT_TEST_GL(TestDrawTextOnPathZigZag); - UNIT_TEST_GL(TestDrawTextOnPathWithOffset); +// UNIT_TEST_GL(TestDrawTextOnPathInteractive); +// UNIT_TEST_GL(TestDrawTextOnPathBigSymbols); +// UNIT_TEST_GL(TestDrawTextOnPath); +// UNIT_TEST_GL(TestDrawTextOnPathZigZag); +// UNIT_TEST_GL(TestDrawTextOnPathWithOffset); + UNIT_TEST_GL(TestDrawStraightTextElement); + UNIT_TEST_GL(TestDrawPathTextElement); // UNIT_TEST_GL(TestDrawTextOverflow); // UNIT_TEST_GL(TestDrawTextFiltering); // UNIT_TEST_GL(TestDrawRandomTextFiltering);