forked from organicmaps/organicmaps
PathTextElement and StraightTextElement implementation.
This commit is contained in:
parent
7435ad01cf
commit
76880dbdf5
16 changed files with 647 additions and 322 deletions
|
@ -236,7 +236,7 @@ namespace fwork
|
|||
if (fontSize != 0)
|
||||
{
|
||||
double textLength = m_glyphCache->getTextLength(fontSize, ptr->m_name);
|
||||
typedef calc_length<base_screen> functor_t;
|
||||
typedef calc_length<base_global> functor_t;
|
||||
functor_t::params p1;
|
||||
p1.m_convertor = &m_convertor;
|
||||
p1.m_rect = &m_rect;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
using std::istringstream;
|
||||
using std::ostringstream;
|
||||
using std::stringstream;
|
||||
using std::endl;
|
||||
|
||||
#ifdef DEBUG_NEW
|
||||
|
|
|
@ -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<ResourceManager> const & resourceManager,
|
||||
shared_ptr<Skin> 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<CharStyle const *>(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<ResourceManager> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<GlyphLayoutElem> m_entries;
|
||||
|
||||
m2::RectD m_limitRect;
|
||||
|
||||
double getKerning(GlyphLayoutElem const & prevElem, GlyphLayoutElem const & curElem);
|
||||
|
||||
public:
|
||||
|
||||
GlyphLayout(GlyphLayout const & layout, double shift);
|
||||
|
||||
GlyphLayout(shared_ptr<ResourceManager> const & resourceManager,
|
||||
shared_ptr<Skin> const & skin,
|
||||
FontDesc const & font,
|
||||
m2::PointD const & pt,
|
||||
wstring const & text,
|
||||
yg::EPosition pos);
|
||||
|
||||
GlyphLayout(shared_ptr<ResourceManager> const & resourceManager,
|
||||
FontDesc const & font,
|
||||
m2::PointD const * pts,
|
||||
|
@ -56,5 +67,7 @@ namespace yg
|
|||
vector<GlyphLayoutElem> const & entries() const;
|
||||
|
||||
m2::RectD const limitRect() const;
|
||||
|
||||
void offset(m2::PointD const & offs);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
}
|
134
yg/text_element.cpp
Normal file
134
yg/text_element.cpp
Normal file
|
@ -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> 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<CharStyle const *>(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);
|
||||
}
|
||||
}
|
118
yg/text_element.hpp
Normal file
118
yg/text_element.hpp
Normal file
|
@ -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<ResourceManager> m_rm;
|
||||
shared_ptr<Skin> m_skin;
|
||||
|
||||
public:
|
||||
|
||||
struct Params : OverlayElement::Params
|
||||
{
|
||||
FontDesc m_fontDesc;
|
||||
string m_utf8Text;
|
||||
double m_depth;
|
||||
bool m_log2vis;
|
||||
shared_ptr<ResourceManager> m_rm;
|
||||
shared_ptr<Skin> 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);
|
||||
};
|
||||
}
|
|
@ -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<Skin> const & skin,
|
||||
shared_ptr<ResourceManager> 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<CharStyle const *>(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;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
|
@ -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<Skin> m_skin;
|
||||
shared_ptr<ResourceManager> m_rm;
|
||||
bool m_log2vis;
|
||||
m2::RectD m_limitRect;
|
||||
|
||||
public:
|
||||
|
||||
TextLayoutElement(
|
||||
char const * text,
|
||||
double depth,
|
||||
FontDesc const & fontDesc,
|
||||
bool log2vis,
|
||||
shared_ptr<Skin> const & skin,
|
||||
shared_ptr<ResourceManager> const & rm,
|
||||
m2::PointD const & pivot,
|
||||
yg::EPosition pos);
|
||||
|
||||
m2::RectD const boundRect() const;
|
||||
|
||||
void draw(Screen * screen);
|
||||
};
|
||||
}
|
|
@ -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<TextPath> const & path,
|
||||
char const * text,
|
||||
double depth,
|
||||
FontDesc const & fontDesc);
|
||||
|
||||
m2::RectD const boundRect() const;
|
||||
|
||||
void draw(Screen * screen);
|
||||
};
|
||||
}
|
122
yg/text_path.cpp
Normal file
122
yg/text_path.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
40
yg/text_path.hpp
Normal file
40
yg/text_path.hpp
Normal file
|
@ -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);
|
||||
};
|
||||
}
|
10
yg/yg.pro
10
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
|
||||
|
|
|
@ -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<m2::PointD> 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<yg::gl::Screen> 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<m2::PointD> 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<yg::gl::Screen> 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<m2::PointD> 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);
|
||||
|
|
Loading…
Add table
Reference in a new issue