From dadd320e569026d70c59b2259900f8ebc000ab2e Mon Sep 17 00:00:00 2001 From: rachytski Date: Tue, 3 May 2011 23:34:00 +0300 Subject: [PATCH] extracted code that performs string on path layout into GlyphLayout class. --- yg/glyph_layout.cpp | 156 ++++++++++++++++++++++++++++++++++ yg/glyph_layout.hpp | 55 ++++++++++++ yg/text_renderer.cpp | 116 ++----------------------- yg/yg.pro | 6 +- yg/yg_tests/screengl_test.cpp | 12 ++- 5 files changed, 233 insertions(+), 112 deletions(-) create mode 100644 yg/glyph_layout.cpp create mode 100644 yg/glyph_layout.hpp diff --git a/yg/glyph_layout.cpp b/yg/glyph_layout.cpp new file mode 100644 index 0000000000..4fd705b896 --- /dev/null +++ b/yg/glyph_layout.cpp @@ -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 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 const & GlyphLayout::entries() const + { + return m_entries; + } +} diff --git a/yg/glyph_layout.hpp b/yg/glyph_layout.hpp new file mode 100644 index 0000000000..6de008215f --- /dev/null +++ b/yg/glyph_layout.hpp @@ -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 m_resourceManager; + + size_t m_firstVisible; + size_t m_lastVisible; + + vector m_entries; + + public: + + GlyphLayout(GlyphLayout const & layout, double shift); + + GlyphLayout(shared_ptr 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 const & entries() const; + }; +} diff --git a/yg/text_renderer.cpp b/yg/text_renderer.cpp index bfcb18e805..cc324348b7 100644 --- a/yg/text_renderer.cpp +++ b/yg/text_renderer.cpp @@ -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 const & glyphs = layout.entries(); - vector 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(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); diff --git a/yg/yg.pro b/yg/yg.pro index b1e66a3319..957b6b8f47 100644 --- a/yg/yg.pro +++ b/yg/yg.pro @@ -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 diff --git a/yg/yg_tests/screengl_test.cpp b/yg/yg_tests/screengl_test.cpp index 2e9e6ec185..a0defaee3a 100644 --- a/yg/yg_tests/screengl_test.cpp +++ b/yg/yg_tests/screengl_test.cpp @@ -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 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);