diff --git a/yg/glyph_layout.cpp b/yg/glyph_layout.cpp index 4fd705b896..d728ad62f5 100644 --- a/yg/glyph_layout.cpp +++ b/yg/glyph_layout.cpp @@ -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 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; } } diff --git a/yg/text_renderer.cpp b/yg/text_renderer.cpp index cc324348b7..f9e5465b1f 100644 --- a/yg/text_renderer.cpp +++ b/yg/text_renderer.cpp @@ -367,6 +367,9 @@ namespace yg vector 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); diff --git a/yg/yg_tests/screengl_test.cpp b/yg/yg_tests/screengl_test.cpp index a0defaee3a..70b0bc2ec4 100644 --- a/yg/yg_tests/screengl_test.cpp +++ b/yg/yg_tests/screengl_test.cpp @@ -729,6 +729,33 @@ namespace return ret; } + struct TestDrawTextOnPathBigSymbols + { + vector 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 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 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 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 m_pathUnder; + vector 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 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);