forked from organicmaps/organicmaps-tmp
extracted code that performs string on path layout into GlyphLayout class.
This commit is contained in:
parent
c7626ce801
commit
dadd320e56
5 changed files with 233 additions and 112 deletions
156
yg/glyph_layout.cpp
Normal file
156
yg/glyph_layout.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
#include "../base/SRC_FIRST.hpp"
|
||||
|
||||
#include "glyph_layout.hpp"
|
||||
#include "resource_manager.hpp"
|
||||
#include "font_desc.hpp"
|
||||
|
||||
#include "../geometry/angles.hpp"
|
||||
|
||||
namespace yg
|
||||
{
|
||||
class pts_array
|
||||
{
|
||||
m2::PointD const * m_arr;
|
||||
size_t m_size;
|
||||
bool m_reverse;
|
||||
|
||||
public:
|
||||
pts_array(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 size() const { return m_size; }
|
||||
|
||||
m2::PointD 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 operator[](size_t i) const { return get(i); }
|
||||
};
|
||||
|
||||
GlyphLayout::GlyphLayout(shared_ptr<ResourceManager> const & resourceManager,
|
||||
FontDesc const & fontDesc,
|
||||
m2::PointD const * pts,
|
||||
size_t ptsCount,
|
||||
wstring const & text,
|
||||
double fullLength,
|
||||
double pathOffset,
|
||||
yg::EPosition pos)
|
||||
: m_resourceManager(resourceManager),
|
||||
m_firstVisible(0),
|
||||
m_lastVisible(0)
|
||||
{
|
||||
pts_array arrPath(pts, ptsCount, fullLength, pathOffset);
|
||||
|
||||
// get vector of glyphs and calculate string length
|
||||
double strLength = 0.0;
|
||||
size_t count = text.size();
|
||||
m_entries.resize(count);
|
||||
|
||||
for (size_t i = 0; i < m_entries.size(); ++i)
|
||||
{
|
||||
m_entries[i].m_sym = text[i];
|
||||
m_entries[i].m_metrics = m_resourceManager->getGlyphMetrics(GlyphKey(m_entries[i].m_sym, fontDesc.m_size, fontDesc.m_isMasked, yg::Color(0, 0, 0, 0)));
|
||||
strLength += m_entries[i].m_metrics.m_xAdvance;
|
||||
}
|
||||
|
||||
// offset of the text from path's start
|
||||
double offset = (fullLength - strLength) / 2.0;
|
||||
if (offset < 0.0)
|
||||
return;
|
||||
offset -= pathOffset;
|
||||
if (-offset >= strLength)
|
||||
return;
|
||||
|
||||
// find first visible glyph
|
||||
size_t symPos = 0;
|
||||
while (offset < 0 && symPos < count)
|
||||
offset += m_entries[symPos++].m_metrics.m_xAdvance;
|
||||
|
||||
m_firstVisible = symPos;
|
||||
m_lastVisible = count;
|
||||
|
||||
size_t ptPos = 0;
|
||||
bool doInitAngle = true;
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
size_t const oldPtPos = ptPos;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (ptPos + 1 == arrPath.size())
|
||||
{
|
||||
m_firstVisible = m_lastVisible = 0;
|
||||
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];
|
||||
}
|
||||
|
||||
if ((oldPtPos != ptPos) || (doInitAngle))
|
||||
{
|
||||
m_entries[symPos].m_angle = ang::AngleTo(m_entries[symPos].m_pt, arrPath[ptPos + 1]);
|
||||
doInitAngle = false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
size_t GlyphLayout::firstVisible() const
|
||||
{
|
||||
return m_firstVisible;
|
||||
}
|
||||
|
||||
size_t GlyphLayout::lastVisible() const
|
||||
{
|
||||
return m_lastVisible;
|
||||
}
|
||||
|
||||
vector<GlyphLayoutElem> const & GlyphLayout::entries() const
|
||||
{
|
||||
return m_entries;
|
||||
}
|
||||
}
|
55
yg/glyph_layout.hpp
Normal file
55
yg/glyph_layout.hpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
#pragma once
|
||||
|
||||
#include "defines.hpp"
|
||||
|
||||
#include "../std/vector.hpp"
|
||||
#include "../std/string.hpp"
|
||||
#include "../std/shared_ptr.hpp"
|
||||
|
||||
#include "../geometry/point2d.hpp"
|
||||
#include "glyph_cache.hpp"
|
||||
|
||||
|
||||
namespace yg
|
||||
{
|
||||
class ResourceManager;
|
||||
struct FontDesc;
|
||||
|
||||
struct GlyphLayoutElem
|
||||
{
|
||||
wchar_t m_sym;
|
||||
GlyphMetrics m_metrics;
|
||||
double m_angle;
|
||||
m2::PointD m_pt;
|
||||
};
|
||||
|
||||
class GlyphLayout
|
||||
{
|
||||
private:
|
||||
|
||||
shared_ptr<ResourceManager> m_resourceManager;
|
||||
|
||||
size_t m_firstVisible;
|
||||
size_t m_lastVisible;
|
||||
|
||||
vector<GlyphLayoutElem> m_entries;
|
||||
|
||||
public:
|
||||
|
||||
GlyphLayout(GlyphLayout const & layout, double shift);
|
||||
|
||||
GlyphLayout(shared_ptr<ResourceManager> const & resourceManager,
|
||||
FontDesc const & font,
|
||||
m2::PointD const * pts,
|
||||
size_t ptsCount,
|
||||
wstring const & text,
|
||||
double fullLength,
|
||||
double pathOffset,
|
||||
yg::EPosition pos);
|
||||
|
||||
size_t firstVisible() const;
|
||||
size_t lastVisible() const;
|
||||
|
||||
vector<GlyphLayoutElem> const & entries() const;
|
||||
};
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
#include "texture.hpp"
|
||||
#include "skin.hpp"
|
||||
#include "render_state.hpp"
|
||||
#include "glyph_layout.hpp"
|
||||
|
||||
#include "../coding/strutil.hpp"
|
||||
|
||||
|
@ -324,77 +325,6 @@ namespace yg
|
|||
return rect;
|
||||
}
|
||||
|
||||
/// Encapsulate array of points in readable getting direction.
|
||||
class pts_array
|
||||
{
|
||||
m2::PointD const * m_arr;
|
||||
size_t m_size;
|
||||
bool m_reverse;
|
||||
|
||||
public:
|
||||
pts_array(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 size() const { return m_size; }
|
||||
|
||||
m2::PointD 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 operator[](size_t i) const { return get(i); }
|
||||
};
|
||||
|
||||
double const angle_not_inited = -100.0;
|
||||
|
||||
bool CalcPointAndAngle(pts_array const & arr, double offset, size_t & ind, m2::PointD & pt, double & angle)
|
||||
{
|
||||
size_t const oldInd = ind;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (ind + 1 == arr.size())
|
||||
return false;
|
||||
|
||||
double const l = arr[ind + 1].Length(pt);
|
||||
if (offset < l)
|
||||
break;
|
||||
|
||||
offset -= l;
|
||||
pt = arr[++ind];
|
||||
}
|
||||
|
||||
if (oldInd != ind || angle == angle_not_inited)
|
||||
angle = ang::AngleTo(pt, arr[ind + 1]);
|
||||
pt = pt.Move(offset, angle);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TextRenderer::drawPathText(
|
||||
FontDesc const & fontDesc, m2::PointD const * path, size_t s, string const & utf8Text,
|
||||
double fullLength, double pathOffset, yg::EPosition pos, double depth)
|
||||
|
@ -416,14 +346,15 @@ namespace yg
|
|||
return drawPathTextImpl(desc, path, s, utf8Text, fullLength, pathOffset, pos, depth);
|
||||
}
|
||||
|
||||
|
||||
bool TextRenderer::drawPathTextImpl(
|
||||
FontDesc const & fontDesc, m2::PointD const * path, size_t s, string const & utf8Text,
|
||||
double fullLength, double pathOffset, yg::EPosition pos, double depth)
|
||||
{
|
||||
pts_array arrPath(path, s, fullLength, pathOffset);
|
||||
|
||||
wstring const text = Log2Vis(FromUtf8(utf8Text));
|
||||
|
||||
GlyphLayout layout(resourceManager(), fontDesc, path, s, text, fullLength, pathOffset, pos);
|
||||
|
||||
// calculate base line offset
|
||||
float blOffset = 2;
|
||||
switch (pos)
|
||||
|
@ -434,50 +365,21 @@ namespace yg
|
|||
default: blOffset -= fontDesc.m_size / 2; break;
|
||||
}
|
||||
|
||||
size_t const count = text.size();
|
||||
vector<GlyphLayoutElem> const & glyphs = layout.entries();
|
||||
|
||||
vector<GlyphMetrics> glyphs(count);
|
||||
|
||||
// get vector of glyphs and calculate string length
|
||||
double strLength = 0.0;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
for (size_t i = layout.firstVisible(); i < layout.lastVisible(); ++i)
|
||||
{
|
||||
glyphs[i] = resourceManager()->getGlyphMetrics(GlyphKey(text[i], fontDesc.m_size, fontDesc.m_isMasked, yg::Color(0, 0, 0, 0)));
|
||||
strLength += glyphs[i].m_xAdvance;
|
||||
}
|
||||
|
||||
// offset of the text from path's start
|
||||
double offset = (fullLength - strLength) / 2.0;
|
||||
if (offset < 0.0) return false;
|
||||
offset -= pathOffset;
|
||||
if (-offset >= strLength) return false;
|
||||
|
||||
// find first visible glyph
|
||||
size_t i = 0;
|
||||
while (offset < 0 && i < count)
|
||||
offset += glyphs[i++].m_xAdvance;
|
||||
|
||||
size_t ind = 0;
|
||||
m2::PointD ptOrg = arrPath[0];
|
||||
double angle = angle_not_inited;
|
||||
|
||||
// draw visible glyphs
|
||||
for (; i < count; ++i)
|
||||
{
|
||||
if (!CalcPointAndAngle(arrPath, offset, ind, ptOrg, angle))
|
||||
break;
|
||||
|
||||
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);
|
||||
CharStyle const * charStyle = static_cast<CharStyle const *>(skin()->fromID(glyphID));
|
||||
|
||||
drawGlyph(ptOrg, m2::PointD(0.0, 0.0), angle, blOffset, charStyle, depth);
|
||||
|
||||
offset = glyphs[i].m_xAdvance;
|
||||
drawGlyph(glyphs[i].m_pt, m2::PointD(0.0, 0.0), glyphs[i].m_angle, blOffset, charStyle, depth);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void TextRenderer::drawGlyph(m2::PointD const & ptOrg, m2::PointD const & ptGlyph, float angle, float blOffset, CharStyle const * p, double depth)
|
||||
{
|
||||
float x0 = ptGlyph.x + (p->m_xOffset - 1);
|
||||
|
|
|
@ -57,7 +57,8 @@ SOURCES += \
|
|||
circle_info.cpp \
|
||||
area_renderer.cpp \
|
||||
layout_element.cpp \
|
||||
font_desc.cpp
|
||||
font_desc.cpp \
|
||||
glyph_layout.cpp
|
||||
|
||||
HEADERS += \
|
||||
internal/opengl.hpp \
|
||||
|
@ -108,7 +109,8 @@ HEADERS += \
|
|||
circle_info.hpp \
|
||||
area_renderer.hpp \
|
||||
layout_element.hpp \
|
||||
font_desc.hpp
|
||||
font_desc.hpp \
|
||||
glyph_layout.hpp
|
||||
|
||||
win32 {
|
||||
HEADERS += internal/opengl_win32.hpp
|
||||
|
|
|
@ -767,6 +767,11 @@ namespace
|
|||
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));
|
||||
m_text = "Simplicity is the ultimate sophistication. Leonardo Da Vinci.";
|
||||
|
||||
double pat[] = { 2, 2 };
|
||||
|
@ -776,7 +781,8 @@ 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, 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);
|
||||
}
|
||||
};
|
||||
|
@ -1067,7 +1073,7 @@ namespace
|
|||
// UNIT_TEST_GL(TestDrawTextRectWithFixedFont);
|
||||
// UNIT_TEST_GL(TestDrawStringOnString);
|
||||
// UNIT_TEST_GL(TestDrawTextOnPath);
|
||||
// UNIT_TEST_GL(TestDrawTextOnPathZigZag);
|
||||
UNIT_TEST_GL(TestDrawTextOnPathZigZag);
|
||||
// UNIT_TEST_GL(TestDrawTextOnPathWithOffset);
|
||||
// UNIT_TEST_GL(TestDrawTextOverflow);
|
||||
// UNIT_TEST_GL(TestDrawTextFiltering);
|
||||
|
@ -1076,7 +1082,7 @@ namespace
|
|||
// UNIT_TEST_GL(TestDrawPoly);
|
||||
// UNIT_TEST_GL(TestDrawSolidRect);
|
||||
// UNIT_TEST_GL(TestDrawPathWithSkinPageMiss);
|
||||
UNIT_TEST_GL(TestDrawPathWithOffset);
|
||||
// UNIT_TEST_GL(TestDrawPathWithOffset);
|
||||
// UNIT_TEST_GL(TestDrawPathJoin);
|
||||
// UNIT_TEST_GL(TestDrawPathSolid1PX);
|
||||
// UNIT_TEST_GL(TestDrawPathSolid2PX);
|
||||
|
|
Loading…
Add table
Reference in a new issue