forked from organicmaps/organicmaps
refactored text-on-string layout code.
This commit is contained in:
parent
c11e497fc6
commit
0e8efdd557
3 changed files with 161 additions and 41 deletions
|
@ -8,6 +8,25 @@
|
|||
|
||||
namespace yg
|
||||
{
|
||||
struct PathPoint
|
||||
{
|
||||
int m_i;
|
||||
m2::PointD m_pt;
|
||||
PathPoint(int i = -1,
|
||||
m2::PointD const & pt = m2::PointD())
|
||||
: m_i(i),
|
||||
m_pt(pt)
|
||||
{}
|
||||
};
|
||||
|
||||
struct PivotPoint
|
||||
{
|
||||
double m_angle;
|
||||
PathPoint m_pp;
|
||||
PivotPoint(double angle = 0, PathPoint const & pp = PathPoint())
|
||||
{}
|
||||
};
|
||||
|
||||
class pts_array
|
||||
{
|
||||
m2::PointD const * m_arr;
|
||||
|
@ -50,7 +69,77 @@ namespace yg
|
|||
ASSERT ( i < m_size, ("Index out of range") );
|
||||
return m_arr[m_reverse ? m_size - i - 1 : i];
|
||||
}
|
||||
|
||||
m2::PointD operator[](size_t i) const { return get(i); }
|
||||
|
||||
PathPoint const 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));
|
||||
if (offset < l)
|
||||
{
|
||||
res.m_pt = res.m_pt.Move(offset, ang::AngleTo(get(i), get(i + 1)));
|
||||
res.m_i = i;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset -= l;
|
||||
res.m_pt = get(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
PivotPoint findPivotPoint(PathPoint const & pp, GlyphMetrics const & sym)
|
||||
{
|
||||
PivotPoint res;
|
||||
res.m_pp.m_i = -1;
|
||||
m2::PointD pt1 = pp.m_pt;
|
||||
|
||||
double angle = 0;
|
||||
double advance = sym.m_xOffset + sym.m_width / 2.0;
|
||||
|
||||
int j = pp.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 - pp.m_i + 1);
|
||||
res.m_pp.m_pt = pt1;
|
||||
res.m_angle = angle;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
GlyphLayout::GlyphLayout(shared_ptr<ResourceManager> const & resourceManager,
|
||||
|
@ -92,50 +181,33 @@ namespace yg
|
|||
while (offset < 0 && symPos < count)
|
||||
offset += m_entries[symPos++].m_metrics.m_xAdvance;
|
||||
|
||||
m_firstVisible = symPos;
|
||||
m_lastVisible = count;
|
||||
PathPoint startPt = arrPath.offsetPoint(PathPoint(0, arrPath.get(0)), offset);
|
||||
|
||||
size_t ptPos = 0;
|
||||
bool doInitAngle = true;
|
||||
m_firstVisible = symPos;
|
||||
|
||||
for (; symPos < count; ++symPos)
|
||||
{
|
||||
if (symPos > 0)
|
||||
{
|
||||
m_entries[symPos].m_pt = m_entries[symPos - 1].m_pt;
|
||||
m_entries[symPos].m_angle = m_entries[symPos - 1].m_angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_entries[symPos].m_pt = arrPath[0];
|
||||
m_entries[symPos].m_angle = 0; //< will be initialized later
|
||||
}
|
||||
GlyphMetrics const & metrics = m_entries[symPos].m_metrics;
|
||||
|
||||
size_t const oldPtPos = ptPos;
|
||||
if (startPt.m_i == -1)
|
||||
return;
|
||||
|
||||
while (true)
|
||||
if (metrics.m_width != 0)
|
||||
{
|
||||
if (ptPos + 1 == arrPath.size())
|
||||
{
|
||||
m_firstVisible = m_lastVisible = 0;
|
||||
PivotPoint pivotPt = arrPath.findPivotPoint(startPt, metrics);
|
||||
|
||||
if (pivotPt.m_pp.m_i == -1)
|
||||
return;
|
||||
}
|
||||
double const l = arrPath[ptPos + 1].Length(m_entries[symPos].m_pt);
|
||||
if (offset < l)
|
||||
break;
|
||||
|
||||
offset -= l;
|
||||
m_entries[symPos].m_pt = arrPath[++ptPos];
|
||||
m_entries[symPos].m_angle = pivotPt.m_angle;
|
||||
double centerOffset = metrics.m_xOffset + metrics.m_width / 2.0;
|
||||
//m_entries[symPos].m_pt = pivotPt.m_pp.m_pt.Move(-centerOffset, pivotPt.m_angle);
|
||||
m_entries[symPos].m_pt = startPt.m_pt;
|
||||
}
|
||||
|
||||
if ((oldPtPos != ptPos) || (doInitAngle))
|
||||
{
|
||||
m_entries[symPos].m_angle = ang::AngleTo(m_entries[symPos].m_pt, arrPath[ptPos + 1]);
|
||||
doInitAngle = false;
|
||||
}
|
||||
startPt = arrPath.offsetPoint(startPt, metrics.m_xAdvance);
|
||||
|
||||
m_entries[symPos].m_pt = m_entries[symPos].m_pt.Move(offset, m_entries[symPos].m_angle);
|
||||
offset = m_entries[symPos].m_metrics.m_xAdvance;
|
||||
m_lastVisible = symPos + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -367,6 +367,9 @@ namespace yg
|
|||
|
||||
vector<GlyphLayoutElem> const & glyphs = layout.entries();
|
||||
|
||||
if (layout.lastVisible() != text.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = layout.firstVisible(); i < layout.lastVisible(); ++i)
|
||||
{
|
||||
uint32_t const glyphID = skin()->mapGlyph(GlyphKey(text[i], fontDesc.m_size, fontDesc.m_isMasked, fontDesc.m_isMasked ? fontDesc.m_maskColor : fontDesc.m_color), fontDesc.m_isStatic);
|
||||
|
|
|
@ -729,6 +729,33 @@ namespace
|
|||
return ret;
|
||||
}
|
||||
|
||||
struct TestDrawTextOnPathBigSymbols
|
||||
{
|
||||
vector<m2::PointD> m_path;
|
||||
string m_text;
|
||||
yg::PenInfo m_penInfo;
|
||||
|
||||
void Init()
|
||||
{
|
||||
m_path.push_back(m2::PointD(40, 200));
|
||||
m_path.push_back(m2::PointD(90, 200));
|
||||
m_path.push_back(m2::PointD(190, 230));
|
||||
|
||||
m_text = "Sim";
|
||||
|
||||
double pat[2] = {2, 2};
|
||||
m_penInfo = yg::PenInfo(yg::Color(0xFF, 0xFF, 0xFF, 0xFF), 2, &pat[0], ARRAY_SIZE(pat), 0);
|
||||
}
|
||||
|
||||
void DoDraw(shared_ptr<yg::gl::Screen> p)
|
||||
{
|
||||
p->drawPath(&m_path[0], m_path.size(), 0, p->skin()->mapPenInfo(m_penInfo), 1);
|
||||
yg::FontDesc fontDesc(false, 40);
|
||||
|
||||
p->drawPathText(fontDesc, &m_path[0], m_path.size(), m_text, calc_length(m_path), 0.0, yg::EPosCenter, 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct TestDrawTextOnPath
|
||||
{
|
||||
std::vector<m2::PointD> m_path;
|
||||
|
@ -739,7 +766,7 @@ namespace
|
|||
{
|
||||
m_path.push_back(m2::PointD(40, 200));
|
||||
m_path.push_back(m2::PointD(100, 100));
|
||||
m_path.push_back(m2::PointD(300, 100));
|
||||
m_path.push_back(m2::PointD(600, 100));
|
||||
m_path.push_back(m2::PointD(400, 300));
|
||||
m_text = "Simplicity is the ultimate sophistication. Leonardo Da Vinci.";
|
||||
|
||||
|
@ -750,7 +777,7 @@ namespace
|
|||
void DoDraw(shared_ptr<yg::gl::Screen> p)
|
||||
{
|
||||
p->drawPath(&m_path[0], m_path.size(), 0, p->skin()->mapPenInfo(m_penInfo), 0);
|
||||
yg::FontDesc fontDesc(false, 10);
|
||||
yg::FontDesc fontDesc(false, 20);
|
||||
p->drawPathText(fontDesc, &m_path[0], m_path.size(), m_text, calc_length(m_path), 0.0, yg::EPosCenter, 0);
|
||||
}
|
||||
};
|
||||
|
@ -789,15 +816,32 @@ namespace
|
|||
|
||||
struct TestDrawTextOnPathWithOffset : TestDrawTextOnPath
|
||||
{
|
||||
vector<m2::PointD> m_pathUnder;
|
||||
vector<m2::PointD> m_pathAbove;
|
||||
|
||||
TestDrawTextOnPathWithOffset()
|
||||
{
|
||||
copy(m_path.begin(), m_path.end(), back_inserter(m_pathUnder));
|
||||
for (size_t i = 0; i < m_pathUnder.size(); ++i)
|
||||
m_pathUnder[i].y -= 50;
|
||||
|
||||
std::copy(m_path.begin(), m_path.end(), back_inserter(m_pathAbove));
|
||||
for (size_t i = 0; i < m_pathUnder.size(); ++i)
|
||||
m_pathAbove[i].y += 50;
|
||||
}
|
||||
|
||||
void DoDraw(shared_ptr<yg::gl::Screen> p)
|
||||
{
|
||||
p->drawPath(&m_path[0], m_path.size(), 0, p->skin()->mapPenInfo(m_penInfo), 0);
|
||||
TestDrawTextOnPath::DoDraw(p);
|
||||
|
||||
p->drawPath(&m_pathAbove[0], m_pathAbove.size(), 0, p->skin()->mapPenInfo(m_penInfo), 0);
|
||||
p->drawPath(&m_pathUnder[0], m_pathUnder.size(), 0, p->skin()->mapPenInfo(m_penInfo), 0);
|
||||
|
||||
double const len = calc_length(m_path);
|
||||
yg::FontDesc fontDesc(false, 10);
|
||||
yg::FontDesc fontDesc(false, 20);
|
||||
|
||||
p->drawPathText(fontDesc, &m_path[0], m_path.size(), m_text, len, 0.0, yg::EPosAbove, 0);
|
||||
p->drawPathText(fontDesc, &m_path[0], m_path.size(), m_text, len, 0.0, yg::EPosUnder, 0);
|
||||
p->drawPathText(fontDesc, &m_pathAbove[0], m_pathAbove.size(), m_text, len, 0.0, yg::EPosAbove, 0);
|
||||
p->drawPathText(fontDesc, &m_pathUnder[0], m_pathUnder.size(), m_text, len, 0.0, yg::EPosUnder, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1072,9 +1116,10 @@ namespace
|
|||
// UNIT_TEST_GL(TestDrawUnicodeSymbols);
|
||||
// UNIT_TEST_GL(TestDrawTextRectWithFixedFont);
|
||||
// UNIT_TEST_GL(TestDrawStringOnString);
|
||||
// UNIT_TEST_GL(TestDrawTextOnPath);
|
||||
UNIT_TEST_GL(TestDrawTextOnPathZigZag);
|
||||
// UNIT_TEST_GL(TestDrawTextOnPathWithOffset);
|
||||
// UNIT_TEST_GL(TestDrawTextOnPathBigSymbols);
|
||||
UNIT_TEST_GL(TestDrawTextOnPath);
|
||||
UNIT_TEST_GL(TestDrawTextOnPathZigZag);
|
||||
UNIT_TEST_GL(TestDrawTextOnPathWithOffset);
|
||||
// UNIT_TEST_GL(TestDrawTextOverflow);
|
||||
// UNIT_TEST_GL(TestDrawTextFiltering);
|
||||
// UNIT_TEST_GL(TestDrawRandomTextFiltering);
|
||||
|
|
Loading…
Add table
Reference in a new issue