extracted code that performs string on path layout into GlyphLayout class.

This commit is contained in:
rachytski 2011-05-03 23:34:00 +03:00 committed by Alex Zolotarev
parent c7626ce801
commit dadd320e56
5 changed files with 233 additions and 112 deletions

156
yg/glyph_layout.cpp Normal file
View 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
View 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;
};
}

View file

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

View file

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

View file

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