From 16ebd8b12cfb40549c46479ec580dc3faba6aa2c Mon Sep 17 00:00:00 2001 From: rachytski Date: Sat, 4 Jun 2011 20:27:12 +0300 Subject: [PATCH] refactored all text rendering using TextElement subclasses. --- map/framework.cpp | 2 +- map/information_display.cpp | 65 ++++--- yg/glyph_layout.cpp | 36 +++- yg/screen.hpp | 8 +- yg/shape_renderer.hpp | 6 +- yg/text_element.cpp | 72 ++++++-- yg/text_element.hpp | 17 +- yg/text_renderer.cpp | 316 ++++++++++++++-------------------- yg/text_renderer.hpp | 35 ++-- yg/yg_tests/screengl_test.cpp | 70 ++++++-- 10 files changed, 338 insertions(+), 289 deletions(-) diff --git a/map/framework.cpp b/map/framework.cpp index 0a144ab2d9..dc11ef9c2d 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -245,7 +245,7 @@ namespace fwork f.ForEachPointRef(fun, m_zoom); if ((fun.IsExist()) && (fun.m_length > textLength)) { - textLength += 200; + textLength += 50; p.m_startLength = (fun.m_length - textLength) / 2; p.m_endLength = p.m_startLength + textLength; } diff --git a/map/information_display.cpp b/map/information_display.cpp index 2cdf31608b..a6c846bfe5 100644 --- a/map/information_display.cpp +++ b/map/information_display.cpp @@ -5,6 +5,7 @@ #include "../yg/defines.hpp" #include "../yg/skin.hpp" #include "../yg/pen_info.hpp" +#include "../yg/text_element.hpp" #include "../version/version.hpp" @@ -164,7 +165,7 @@ void InformationDisplay::drawRuler(DrawerYG * pDrawer) yg::FontDesc fontDesc = yg::FontDesc::defaultFont; - m2::RectD textRect = pDrawer->screen()->textRect(fontDesc, scalerText.c_str(), false); +// m2::RectD textRect = pDrawer->screen()->textRect(fontDesc, scalerText.c_str(), false); pDrawer->screen()->drawText(fontDesc, scalerPts[1] + m2::PointD(7, -7), yg::EPosAboveRight, @@ -208,29 +209,27 @@ void InformationDisplay::drawCenter(DrawerYG * drawer) out << "(" << fixed << setprecision(4) << m_centerPtLonLat.y << ", " << fixed << setprecision(4) << setw(8) << m_centerPtLonLat.x << ")"; - m2::RectD const & textRect = drawer->screen()->textRect( - yg::FontDesc::defaultFont, - out.str().c_str(), - false); + yg::StraightTextElement::Params params; + params.m_depth = yg::maxDepth; + params.m_fontDesc = yg::FontDesc::defaultFont; + params.m_log2vis = false; + params.m_pivot = m2::PointD(m_displayRect.maxX() - 10 * m_visualScale, + m_displayRect.maxY() - (m_bottomShift + 10) * m_visualScale - 5); + params.m_position = yg::EPosAboveLeft; + params.m_rm = drawer->screen()->resourceManager(); + params.m_skin = drawer->screen()->skin(); + params.m_utf8Text = out.str(); - m2::RectD bgRect = m2::Offset(m2::Inflate(textRect, 5.0, 5.0), - m_displayRect.maxX() - textRect.SizeX() - 10 * m_visualScale, - m_displayRect.maxY() - (m_bottomShift + 10) * m_visualScale - 5); + yg::StraightTextElement ste(params); + + m2::RectD bgRect = m2::Inflate(ste.boundRect(), 5.0, 5.0); drawer->screen()->drawRectangle( bgRect, yg::Color(187, 187, 187, 128), yg::maxDepth - 1); - drawer->screen()->drawText( - yg::FontDesc::defaultFont, - m2::PointD(m_displayRect.maxX() - textRect.SizeX() - 10 * m_visualScale, - m_displayRect.maxY() - (m_bottomShift + 10) * m_visualScale - 5), - yg::EPosAboveRight, - 0, - out.str().c_str(), - yg::maxDepth, - false); + ste.draw(drawer->screen().get()); } void InformationDisplay::enableGlobalRect(bool doEnable) @@ -370,7 +369,7 @@ void InformationDisplay::enableLog(bool doEnable, WindowHandle * windowHandle) } } -void InformationDisplay::drawLog(DrawerYG * pDrawer) +void InformationDisplay::drawLog(DrawerYG * drawer) { threads::MutexGuard guard(s_logMutex); @@ -380,27 +379,25 @@ void InformationDisplay::drawLog(DrawerYG * pDrawer) m2::PointD startPt(m_displayRect.minX() + 10, m_displayRect.minY() + m_yOffset); - m2::RectD textRect = pDrawer->screen()->textRect( - yg::FontDesc::defaultFont, - it->c_str(), - false - ); + yg::StraightTextElement::Params params; + params.m_depth = yg::maxDepth; + params.m_fontDesc = yg::FontDesc::defaultFont; + params.m_log2vis = false; + params.m_pivot = startPt; + params.m_position = yg::EPosAboveRight; + params.m_rm = drawer->screen()->resourceManager(); + params.m_skin = drawer->screen()->skin(); + params.m_utf8Text = *it; - pDrawer->screen()->drawRectangle( - m2::Inflate(m2::Offset(textRect, startPt), m2::PointD(2, 2)), + yg::StraightTextElement ste(params); + + drawer->screen()->drawRectangle( + m2::Inflate(ste.boundRect(), m2::PointD(2, 2)), yg::Color(0, 0, 0, 128), yg::maxDepth - 1 ); - pDrawer->screen()->drawText( - yg::FontDesc::defaultFont, - startPt, - yg::EPosAboveRight, - 0, - it->c_str(), - yg::maxDepth, - false - ); + ste.draw(drawer->screen().get()); } } diff --git a/yg/glyph_layout.cpp b/yg/glyph_layout.cpp index fd9766973f..514b6465f7 100644 --- a/yg/glyph_layout.cpp +++ b/yg/glyph_layout.cpp @@ -213,14 +213,14 @@ namespace yg { if (i == 0) m_limitRect = m2::RectD(p->m_xOffset + pv.x, - p->m_yOffset + pv.y, + -p->m_yOffset + pv.y, p->m_xOffset + pv.x, - p->m_yOffset + pv.y); + -p->m_yOffset + pv.y); else - m_limitRect.Add(m2::PointD(p->m_xOffset, p->m_yOffset) + pv); + m_limitRect.Add(m2::PointD(p->m_xOffset, -p->m_yOffset) + pv); m_limitRect.Add(m2::PointD(p->m_xOffset + p->m_texRect.SizeX() - 4, - p->m_yOffset + p->m_texRect.SizeY() - 4) + pv); + -(p->m_yOffset + (int)p->m_texRect.SizeY() - 4)) + pv); } @@ -244,12 +244,15 @@ namespace yg { GlyphMetrics const m = resourceManager->getGlyphMetrics(glyphKey); if (i == 0) - m_limitRect = m2::RectD(m.m_xOffset + pv.x, m.m_yOffset + pv.y, m.m_xOffset + pv.x, m.m_yOffset + pv.y); + m_limitRect = m2::RectD(m.m_xOffset + pv.x, + -m.m_yOffset + pv.y, + m.m_xOffset + pv.x, + -m.m_yOffset + pv.y); else - m_limitRect.Add(m2::PointD(m.m_xOffset, m.m_yOffset) + pv); + m_limitRect.Add(m2::PointD(m.m_xOffset, -m.m_yOffset) + pv); - m_limitRect.Add(m2::PointD(m.m_xOffset + m.m_xAdvance, - m.m_yOffset + m.m_yAdvance) + pv); + m_limitRect.Add(m2::PointD(m.m_xOffset + m.m_width, + -(m.m_yOffset + m.m_height)) + pv); GlyphLayoutElem elem; elem.m_sym = text[i]; @@ -262,6 +265,8 @@ namespace yg } } + m_limitRect.Inflate(2, 2); + m2::PointD ptOffs(-m_limitRect.SizeX() / 2, -m_limitRect.SizeY() / 2); @@ -399,6 +404,18 @@ namespace yg // < align to baseline // m_entries[symPos].m_pt = m_entries[symPos].m_pt.Move(blOffset - kernOffset, m_entries[symPos].m_angle - math::pi / 2); } + else + { + if (symPos == m_firstVisible) + { + m_firstVisible = symPos + 1; + } + else + { + m_entries[symPos].m_angle = 0; + m_entries[symPos].m_pt = glyphStartPt.m_pt; + } + } glyphStartPt = arrPath.offsetPoint(glyphStartPt, fullGlyphAdvance); offset += fullGlyphAdvance; @@ -422,7 +439,10 @@ namespace yg symRectAA.GetGlobalPoints(pts); if (isFirst) + { m_limitRect = m2::RectD(pts[0].x, pts[0].y, pts[0].x, pts[0].y); + isFirst = false; + } else m_limitRect.Add(pts[0]); diff --git a/yg/screen.hpp b/yg/screen.hpp index f7b7082357..3554e8bc58 100644 --- a/yg/screen.hpp +++ b/yg/screen.hpp @@ -1,20 +1,20 @@ #pragma once -#include "shape_renderer.hpp" +#include "symbol_renderer.hpp" namespace yg { class ResourceManager; namespace gl { - class Screen : public ShapeRenderer + class Screen : public SymbolRenderer { private: public: - typedef ShapeRenderer::Params Params; + typedef SymbolRenderer::Params Params; - Screen(Params const & params) : ShapeRenderer(params) + Screen(Params const & params) : SymbolRenderer(params) {} }; } diff --git a/yg/shape_renderer.hpp b/yg/shape_renderer.hpp index 3a45ac8e89..6a8d203b85 100644 --- a/yg/shape_renderer.hpp +++ b/yg/shape_renderer.hpp @@ -1,15 +1,15 @@ #pragma once -#include "symbol_renderer.hpp" +#include "path_renderer.hpp" namespace yg { namespace gl { - class ShapeRenderer : public SymbolRenderer + class ShapeRenderer : public PathRenderer { private: - typedef SymbolRenderer base_t; + typedef PathRenderer base_t; public: ShapeRenderer(base_t::Params const & params); diff --git a/yg/text_element.cpp b/yg/text_element.cpp index 498ce1f1a3..050b7a7b98 100644 --- a/yg/text_element.cpp +++ b/yg/text_element.cpp @@ -3,10 +3,15 @@ #include "text_element.hpp" #include "screen.hpp" #include "skin.hpp" +#include "text_renderer.hpp" #include "resource_style.hpp" +#include "../3party/fribidi/lib/fribidi-deprecated.h" + +#include "../base/logging.hpp" #include "../base/string_utils.hpp" + namespace yg { OverlayElement::OverlayElement(Params const & p) @@ -38,6 +43,16 @@ namespace yg m_position = pos; } + wstring const TextElement::log2vis(wstring const & str) + { + size_t const count = str.size(); + wstring res; + res.resize(count); + FriBidiParType dir = FRIBIDI_PAR_LTR; // requested base direction + fribidi_log2vis(str.c_str(), count, &dir, &res[0], 0, 0, 0); + return res; + } + TextElement::TextElement(Params const & p) : OverlayElement(p), m_fontDesc(p.m_fontDesc), @@ -49,7 +64,22 @@ namespace yg { } - void TextElement::drawTextImpl(GlyphLayout const & layout, gl::Screen * screen, FontDesc const & fontDesc) + string const & TextElement::utf8Text() const + { + return m_utf8Text; + } + + FontDesc const & TextElement::fontDesc() const + { + return m_fontDesc; + } + + double TextElement::depth() const + { + return m_depth; + } + + void TextElement::drawTextImpl(GlyphLayout const & layout, gl::TextRenderer * screen, FontDesc const & fontDesc, double depth) const { for (unsigned i = layout.firstVisible(); i < layout.lastVisible(); ++i) { @@ -58,7 +88,7 @@ namespace yg uint32_t const glyphID = skin->mapGlyph(GlyphKey(elem.m_sym, 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)); - screen->drawGlyph(elem.m_pt, m2::PointD(0.0, 0.0), elem.m_angle, 0, charStyle, m_depth); + screen->drawGlyph(elem.m_pt, m2::PointD(0.0, 0.0), elem.m_angle, 0, charStyle, depth); } } @@ -68,7 +98,7 @@ namespace yg p.m_skin, p.m_fontDesc, p.m_pivot, - strings::FromUtf8(p.m_utf8Text), + p.m_log2vis ? log2vis(strings::FromUtf8(p.m_utf8Text)) : strings::FromUtf8(p.m_utf8Text), p.m_position) { } @@ -78,16 +108,21 @@ namespace yg return m_glyphLayout.limitRect(); } - void StraightTextElement::draw(gl::Screen * screen) + void StraightTextElement::draw(gl::TextRenderer * screen) const { yg::FontDesc desc = m_fontDesc; if (m_fontDesc.m_isMasked) { - drawTextImpl(m_glyphLayout, screen, m_fontDesc); + drawTextImpl(m_glyphLayout, screen, m_fontDesc, yg::maxDepth); desc.m_isMasked = false; } - drawTextImpl(m_glyphLayout, screen, desc); + drawTextImpl(m_glyphLayout, screen, desc, yg::maxDepth); + } + + void StraightTextElement::draw(gl::Screen * screen) const + { + draw((gl::TextRenderer*)screen); } void StraightTextElement::offset(m2::PointD const & offs) @@ -102,11 +137,13 @@ namespace yg p.m_fontDesc, p.m_pts, p.m_ptsCount, - strings::FromUtf8(p.m_utf8Text), + p.m_log2vis ? log2vis(strings::FromUtf8(p.m_utf8Text)) : strings::FromUtf8(p.m_utf8Text), p.m_fullLength, p.m_pathOffset, p.m_position) { + m_pts.resize(p.m_ptsCount); + copy(p.m_pts, p.m_pts + p.m_ptsCount, m_pts.begin()); } m2::RectD const PathTextElement::boundRect() const @@ -114,20 +151,35 @@ namespace yg return m_glyphLayout.limitRect(); } - void PathTextElement::draw(gl::Screen * screen) + void PathTextElement::draw(gl::TextRenderer * screen) const { +/* yg::PenInfo penInfo(yg::Color(0, 0, 0, 255), 2, 0, 0, 0); + screen->drawPath(&m_pts[0], m_pts.size(), 0, screen->skin()->mapPenInfo(penInfo), yg::maxDepth - 2); + if (boundRect().SizeX() > 500) + { + LOG(LINFO, (strings::FromUtf8(utf8Text()).c_str())); + } + screen->drawRectangle(boundRect(), yg::Color(rand() % 255, rand() % 255, rand() % 255, 64), yg::maxDepth - 3); +*/ yg::FontDesc desc = m_fontDesc; if (m_fontDesc.m_isMasked) { - drawTextImpl(m_glyphLayout, screen, m_fontDesc); + drawTextImpl(m_glyphLayout, screen, m_fontDesc, yg::maxDepth); desc.m_isMasked = false; } - drawTextImpl(m_glyphLayout, screen, desc); + drawTextImpl(m_glyphLayout, screen, desc, yg::maxDepth); + } + + void PathTextElement::draw(gl::Screen * screen) const + { + draw((gl::TextRenderer*)screen); } void PathTextElement::offset(m2::PointD const & offs) { + for (unsigned i = 0; i < m_pts.size(); ++i) + m_pts[i] += offs; TextElement::offset(offs); m_glyphLayout.offset(offs); } diff --git a/yg/text_element.hpp b/yg/text_element.hpp index 93097850a7..ef0cb283b2 100644 --- a/yg/text_element.hpp +++ b/yg/text_element.hpp @@ -17,6 +17,7 @@ namespace yg namespace gl { class Screen; + class TextRenderer; } class OverlayElement @@ -38,7 +39,7 @@ namespace yg virtual void offset(m2::PointD const & offs) = 0; virtual m2::RectD const boundRect() const = 0; - virtual void draw(gl::Screen * screen) = 0; + virtual void draw(gl::Screen * screen) const = 0; m2::PointD const & pivot() const; void setPivot(m2::PointD const & pv); @@ -73,7 +74,11 @@ namespace yg TextElement(Params const & p); - void drawTextImpl(GlyphLayout const & layout, gl::Screen * screen, FontDesc const & desc); + void drawTextImpl(GlyphLayout const & layout, gl::TextRenderer * screen, FontDesc const & desc, double depth) const; + wstring const log2vis(wstring const & str); + string const & utf8Text() const; + FontDesc const & fontDesc() const; + double depth() const; }; class StraightTextElement : public TextElement @@ -90,7 +95,8 @@ namespace yg StraightTextElement(Params const & p); m2::RectD const boundRect() const; - void draw(gl::Screen * screen); + void draw(gl::Screen * screen) const; + void draw(gl::TextRenderer * screen) const; void offset(m2::PointD const & offs); }; @@ -99,8 +105,10 @@ namespace yg private: GlyphLayout m_glyphLayout; + vector m_pts; public: + struct Params : TextElement::Params { m2::PointD const * m_pts; @@ -112,7 +120,8 @@ namespace yg PathTextElement(Params const & p); m2::RectD const boundRect() const; - void draw(gl::Screen * screen); + void draw(gl::Screen * screen) const; + void draw(gl::TextRenderer * screen) const; void offset(m2::PointD const & offs); }; } diff --git a/yg/text_renderer.cpp b/yg/text_renderer.cpp index 1235d83976..0aa5615372 100644 --- a/yg/text_renderer.cpp +++ b/yg/text_renderer.cpp @@ -10,18 +10,15 @@ #include "../std/bind.hpp" -#include "../3party/fribidi/lib/fribidi-deprecated.h" #include "../base/string_utils.hpp" #include "../base/logging.hpp" #include "../base/stl_add.hpp" - namespace yg { namespace gl { - TextRenderer::Params::Params() : m_textTreeAutoClean(true), m_useTextTree(false), @@ -38,8 +35,8 @@ namespace yg m_doPeriodicalTextUpdate(params.m_doPeriodicalTextUpdate) {} - TextRenderer::TextObj::TextObj(FontDesc const & fontDesc, m2::PointD const & pt, yg::EPosition pos, string const & txt, double d, bool log2vis) - : m_fontDesc(fontDesc), m_pt(pt), m_pos(pos), m_utf8Text(txt), m_depth(d), m_needRedraw(true), m_frozen(false), m_log2vis(log2vis) + TextRenderer::TextObj::TextObj(StraightTextElement const & elem) + : m_elem(elem), m_needRedraw(true), m_frozen(false) { } @@ -49,34 +46,14 @@ namespace yg /// lies inside the testing rect and therefore should be skipped. if (m_needRedraw) { - pTextRenderer->drawTextImpl(m_fontDesc, m_pt, m_pos, 0.0, m_utf8Text, yg::maxDepth, m_log2vis); + m_elem.draw(pTextRenderer); m_frozen = true; } } m2::RectD const TextRenderer::TextObj::GetLimitRect(TextRenderer* pTextRenderer) const { - m2::RectD limitRect = pTextRenderer->textRect(m_fontDesc, m_utf8Text, m_log2vis); - - double dx = -limitRect.SizeX() / 2; - double dy = limitRect.SizeY() / 2; - - if (m_pos & EPosLeft) - dx = -limitRect.SizeX(); - - if (m_pos & EPosRight) - dx = 0; - - if (m_pos & EPosUnder) - dy = limitRect.SizeY(); - - if (m_pos & EPosAbove) - dy = 0; - - dx = ::floor(dx); - dy = ::floor(dy); - - return m2::Offset(limitRect, m_pt + m2::PointD(dx, dy)); + return m_elem.boundRect(); } void TextRenderer::TextObj::SetNeedRedraw(bool flag) const @@ -96,12 +73,12 @@ namespace yg string const & TextRenderer::TextObj::Text() const { - return m_utf8Text; + return m_elem.utf8Text(); } void TextRenderer::TextObj::Offset(m2::PointD const & offs) { - m_pt += offs; + m_elem.offset(offs); } bool TextRenderer::TextObj::better_text(TextObj const & r1, TextObj const & r2) @@ -110,9 +87,9 @@ namespace yg // because frozen texts shouldn't be popped out by newly arrived texts. if (r2.m_frozen) return false; - if (r1.m_fontDesc != r2.m_fontDesc) - return r1.m_fontDesc > r2.m_fontDesc; - return (r1.m_depth > r2.m_depth); + if (r1.m_elem.fontDesc() != r2.m_elem.fontDesc()) + return r1.m_elem.fontDesc() > r2.m_elem.fontDesc(); + return (r1.m_elem.depth() > r2.m_elem.depth()); } void TextRenderer::drawText(FontDesc const & fontDesc, @@ -126,12 +103,24 @@ namespace yg if (!m_drawTexts) return; + StraightTextElement::Params params; + params.m_depth = depth; + params.m_fontDesc = fontDesc; + params.m_log2vis = log2vis; + params.m_pivot = pt; + params.m_position = pos; + params.m_rm = resourceManager(); + params.m_skin = skin(); + params.m_utf8Text = utf8Text; + + StraightTextElement ste(params); + if (!m_useTextTree || fontDesc.m_isStatic) - drawTextImpl(fontDesc, pt, pos, angle, utf8Text, depth, log2vis); + ste.draw(this); else { checkTextRedraw(); - TextObj obj(fontDesc, pt, pos, utf8Text, depth, log2vis); + TextObj obj(ste); m2::RectD r = obj.GetLimitRect(this); m_tree.ReplaceIf(obj, r, &TextObj::better_text); } @@ -144,6 +133,7 @@ namespace yg { m_needTextRedraw = false; m_tree.ForEach(bind(&TextObj::Draw, _1, this)); + /// flushing only texts base_t::flush(skin()->currentTextPage()); } @@ -167,8 +157,45 @@ namespace yg if (m_useTextTree) { m_tree.ForEach(bind(&TextObj::Draw, _1, this)); + + unsigned pathTextDrawn = 0; + unsigned pathTextGroups = 0; + unsigned maxGroup = 0; + + list toErase; + + for (path_text_elements::const_iterator it = m_pathTexts.begin(); it != m_pathTexts.end(); ++it) + { + list const & l = it->second; + + unsigned curGroup = 0; + + for (list::const_iterator j = l.begin(); j != l.end(); ++j) + { + j->draw(this); + ++pathTextDrawn; + } + + if (l.empty()) + toErase.push_back(it->first); + + ++pathTextGroups; + + if (maxGroup < l.size()) + maxGroup = l.size(); + + } + + for (list::const_iterator it = toErase.begin(); it != toErase.end(); ++it) + m_pathTexts.erase(*it); + + LOG(LINFO, ("text on pathes: ", pathTextDrawn, ", groups: ", pathTextGroups, ", max group:", maxGroup)); + if (m_textTreeAutoClean) + { m_tree.Clear(); + m_pathTexts.clear(); + } m_needTextRedraw = false; } @@ -179,9 +206,10 @@ namespace yg { ASSERT(m_useTextTree, ()); m_tree.Clear(); + m_pathTexts.clear(); } - void TextRenderer::offsetTextTree(m2::PointD const & offs, m2::RectD const & rect) + void TextRenderer::offsetTexts(m2::PointD const & offs, m2::RectD const & rect) { ASSERT(m_useTextTree, ()); vector texts; @@ -206,6 +234,38 @@ namespace yg } } + void TextRenderer::offsetPathTexts(m2::PointD const & offs, m2::RectD const & rect) + { + ASSERT(m_useTextTree, ()); + + for (path_text_elements::iterator i = m_pathTexts.begin(); i != m_pathTexts.end(); ++i) + { + list & l = i->second; + list::iterator it = l.begin(); + while (it != l.end()) + { + it->offset(offs); + m2::RectD const & r = it->boundRect(); + if (!rect.IsIntersect(r) && !rect.IsRectInside(r)) + { + list::iterator tempIt = it; + ++tempIt; + l.erase(it); + it = tempIt; + } + else + ++it; + } + + } + } + + void TextRenderer::offsetTextTree(m2::PointD const & offs, m2::RectD const & rect) + { + offsetTexts(offs, rect); + offsetPathTexts(offs, rect); + } + void TextRenderer::setNeedTextRedraw(bool flag) { ASSERT(m_useTextTree, ()); @@ -219,171 +279,49 @@ namespace yg base_t::updateActualTarget(); } - template - void TextRenderer::ForEachGlyph(FontDesc const & fontDesc, wstring const & text, ToDo toDo) - { - m2::PointD currPt(0, 0); - for (size_t i = 0; i < text.size(); ++i) - { - uint32_t 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 * p = static_cast(skin()->fromID(glyphID)); - if (p) - { - toDo(currPt, p); - currPt += m2::PointD(p->m_xAdvance, 0); - } - } - } - - wstring TextRenderer::Log2Vis(wstring const & str) - { - size_t const count = str.size(); - wstring res; - res.resize(count); - FriBidiParType dir = FRIBIDI_PAR_LTR; // requested base direction - fribidi_log2vis(str.c_str(), count, &dir, &res[0], 0, 0, 0); - return res; - } - - void TextRenderer::drawTextImpl(FontDesc const & fontDesc, m2::PointD const & pt, yg::EPosition pos, float angle, string const & utf8Text, double depth, bool log2vis) - { - wstring text = strings::FromUtf8(utf8Text); - - if (log2vis) - text = Log2Vis(text); - - m2::RectD r = textRect(fontDesc, utf8Text, log2vis); - - m2::PointD orgPt(pt.x - r.SizeX() / 2, pt.y + r.SizeY() / 2); - - if (pos & EPosLeft) - orgPt.x = pt.x - r.SizeX(); - - if (pos & EPosRight) - orgPt.x = pt.x; - - if (pos & EPosUnder) - orgPt.y = pt.y + r.SizeY(); - - if (pos & EPosAbove) - orgPt.y = pt.y; - - orgPt.x = ::floor(orgPt.x); - orgPt.y = ::floor(orgPt.y); - - yg::FontDesc desc = fontDesc; - - if (desc.m_isMasked) - { - ForEachGlyph(desc, text, bind(&TextRenderer::drawGlyph, this, cref(orgPt), _1, angle, 0, _2, depth)); - desc.m_isMasked = false; - } - - ForEachGlyph(desc, text, bind(&TextRenderer::drawGlyph, this, cref(orgPt), _1, angle, 0, _2, depth)); - } - - m2::RectD const TextRenderer::textRect(FontDesc const & fontDesc, string const & utf8Text, bool log2vis) - { - if (m_useTextTree) - checkTextRedraw(); - - m2::RectD rect; - m2::PointD pt(0, 0); - - wstring text = strings::FromUtf8(utf8Text); - if (log2vis) - text = Log2Vis(text); - - for (size_t i = 0; i < text.size(); ++i) - { - if (fontDesc.m_isStatic) - { - uint32_t glyphID = skin()->mapGlyph(GlyphKey(text[i], fontDesc.m_size, fontDesc.m_isMasked, yg::Color(0, 0, 0, 0)), fontDesc.m_isStatic); - CharStyle const * p = static_cast(skin()->fromID(glyphID)); - if (p != 0) - { - rect.Add(pt); - rect.Add(pt + m2::PointD(p->m_xOffset + p->m_texRect.SizeX() - 4, -p->m_yOffset - (int)p->m_texRect.SizeY() + 4)); - pt += m2::PointD(p->m_xAdvance, 0); - } - } - else - { - GlyphMetrics const m = resourceManager()->getGlyphMetrics(GlyphKey(text[i], fontDesc.m_size, fontDesc.m_isMasked, yg::Color(0, 0, 0, 0))); - - rect.Add(pt); - rect.Add(pt + m2::PointD(m.m_xOffset + m.m_width, - m.m_yOffset - m.m_height)); - pt += m2::PointD(m.m_xAdvance, 0); - } - } - - rect.Inflate(2, 2); - - return rect; - } - 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) { if (!m_drawTexts) return false; - if (m_useTextTree) + + PathTextElement::Params params; + + params.m_pts = path; + params.m_ptsCount = s; + params.m_fullLength = fullLength; + params.m_pathOffset = pathOffset; + params.m_fontDesc = fontDesc; + params.m_utf8Text = utf8Text; + params.m_depth = depth; + params.m_log2vis = true; + params.m_rm = resourceManager(); + params.m_skin = skin(); + params.m_pivot = path[0]; + params.m_position = pos; + + PathTextElement pte(params); + + if (!m_useTextTree || fontDesc.m_isStatic) + pte.draw(this); + else + { checkTextRedraw(); - yg::FontDesc desc = fontDesc; + list & l = m_pathTexts[utf8Text]; - if (desc.m_isMasked) - { - if (!drawPathTextImpl(desc, path, s, utf8Text, fullLength, pathOffset, pos, depth)) - return false; - else - desc.m_isMasked = false; - } - return drawPathTextImpl(desc, path, s, utf8Text, fullLength, pathOffset, pos, depth); - } + bool doAppend = true; + for (list::const_iterator it = l.begin(); it != l.end(); ++it) + if (it->boundRect().IsIntersect(pte.boundRect())) + { + doAppend = false; + break; + } - 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) - { - wstring const text = Log2Vis(strings::FromUtf8(utf8Text)); - - GlyphLayout layout(resourceManager(), fontDesc, path, s, text, fullLength, pathOffset, pos); - - 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 colorID = skin()->mapColor(yg::Color(fontDesc.m_isMasked ? 255 : 0, 0, fontDesc.m_isMasked ? 0 : 255, 255)); - ResourceStyle const * colorStyle = skin()->fromID(colorID); - - float x0 = glyphs[i].m_metrics.m_xOffset; - float y1 = -glyphs[i].m_metrics.m_yOffset; - float y0 = y1 - glyphs[i].m_metrics.m_height; - float x1 = x0 + glyphs[i].m_metrics.m_width; - - drawTexturedPolygon(glyphs[i].m_pt, glyphs[i].m_angle, - colorStyle->m_texRect.minX() + 1, - colorStyle->m_texRect.minY() + 1, - colorStyle->m_texRect.maxX() - 1, - colorStyle->m_texRect.maxY() - 1, - x0, y0, x1, y1, - depth - 1, - colorStyle->m_pageID); - - } -*/ - 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); - CharStyle const * charStyle = static_cast(skin()->fromID(glyphID)); - - drawGlyph(glyphs[i].m_pt, m2::PointD(0.0, 0.0), glyphs[i].m_angle, 0, charStyle, depth); + if (doAppend) + l.push_back(pte); } return true; diff --git a/yg/text_renderer.hpp b/yg/text_renderer.hpp index 65b35df63b..c5b9a697ef 100644 --- a/yg/text_renderer.hpp +++ b/yg/text_renderer.hpp @@ -1,8 +1,9 @@ #pragma once -#include "path_renderer.hpp" +#include "shape_renderer.hpp" #include "defines.hpp" #include "font_desc.hpp" +#include "text_element.hpp" #include "../geometry/tree4d.hpp" @@ -16,29 +17,19 @@ namespace yg { class BaseTexture; - class TextRenderer : public PathRenderer + class TextRenderer : public ShapeRenderer { public: class TextObj { - FontDesc m_fontDesc; - m2::PointD m_pt; - yg::EPosition m_pos; - string m_utf8Text; - double m_depth; + StraightTextElement m_elem; mutable bool m_needRedraw; mutable bool m_frozen; - bool m_log2vis; public: - TextObj(FontDesc const & fontDesc, - m2::PointD const & pt, - yg::EPosition pos, - string const & txt, - double depth, - bool log2vis); + TextObj(StraightTextElement const & elem); void Draw(TextRenderer * pTextRenderer) const; m2::RectD const GetLimitRect(TextRenderer * pTextRenderer) const; void SetNeedRedraw(bool needRedraw) const; @@ -53,15 +44,12 @@ namespace yg private: m4::Tree m_tree; + typedef map > path_text_elements; + path_text_elements m_pathTexts; void checkTextRedraw(); bool m_needTextRedraw; - static wstring Log2Vis(wstring const & str); - - template - void ForEachGlyph(FontDesc const & fontDesc, wstring const & text, ToDo toDo); - bool drawPathTextImpl(FontDesc const & fontDesc, m2::PointD const * path, size_t s, @@ -87,7 +75,7 @@ namespace yg public: - typedef PathRenderer base_t; + typedef ShapeRenderer base_t; struct Params : base_t::Params { @@ -116,10 +104,6 @@ namespace yg double depth, bool log2vis); - m2::RectD const textRect(FontDesc const & fontDesc, - string const & utf8Text, - bool log2vis); - /// Drawing text in the middle of the path. bool drawPathText(FontDesc const & fontDesc, m2::PointD const * path, @@ -142,6 +126,9 @@ namespace yg /// when the new texts arrive void offsetTextTree(m2::PointD const & offs, m2::RectD const & r); + void offsetTexts(m2::PointD const & offs, m2::RectD const & r); + void offsetPathTexts(m2::PointD const & offs, m2::RectD const & r); + /// flush texts upon any function call. void setNeedTextRedraw(bool flag); diff --git a/yg/yg_tests/screengl_test.cpp b/yg/yg_tests/screengl_test.cpp index c3afa540a8..211134414a 100644 --- a/yg/yg_tests/screengl_test.cpp +++ b/yg/yg_tests/screengl_test.cpp @@ -698,22 +698,67 @@ namespace } }; + struct TestDrawTextRect : TestDrawString + { + typedef TestDrawString base_t; + void DoDraw(shared_ptr p) + { + m2::PointD startPt(40, 50); + + yg::StraightTextElement::Params params; + params.m_depth = 0; + params.m_fontDesc = yg::FontDesc(false, 20, yg::Color(0, 0, 0, 0), true, yg::Color(255, 255, 255, 255)); + params.m_log2vis = false; + params.m_pivot = startPt; + params.m_position = yg::EPosAboveRight; + params.m_rm = p->resourceManager(); + params.m_skin = p->skin(); + params.m_utf8Text = "Simplicity is the ultimate sophistication"; + yg::StraightTextElement ste(params); + + m2::RectD r = ste.boundRect(); + + m2::PointD pts[6] = { + m2::PointD(r.minX(), r.minY()), + m2::PointD(r.maxX(), r.minY()), + m2::PointD(r.maxX(), r.maxY()), + m2::PointD(r.minX(), r.minY()), + m2::PointD(r.maxX(), r.maxY()), + m2::PointD(r.minX(), r.maxY()) + }; + + p->drawTrianglesList(pts, 6, p->skin()->mapColor(yg::Color(0, 0, 255, 255)), 0); + + base_t::DoDraw(p); + } + }; + struct TestDrawTextRectWithFixedFont : TestDrawStringWithFixedFont { typedef TestDrawStringWithFixedFont base_t; void DoDraw(shared_ptr p) { - m2::RectD r = p->textRect(yg::FontDesc::defaultFont, "Simplicity is the ultimate sophistication", false); - m2::PointD startPt(40, 50); + yg::StraightTextElement::Params params; + params.m_depth = 0; + params.m_fontDesc = yg::FontDesc::defaultFont; + params.m_log2vis = false; + params.m_pivot = startPt; + params.m_position = yg::EPosAboveRight; + params.m_rm = p->resourceManager(); + params.m_skin = p->skin(); + params.m_utf8Text = "Simplicity is the ultimate sophistication"; + yg::StraightTextElement ste(params); + + m2::RectD r = ste.boundRect(); m2::PointD pts[6] = { - startPt + m2::PointD(r.minX(), r.minY()), - startPt + m2::PointD(r.maxX(), r.minY()), - startPt + m2::PointD(r.maxX(), r.maxY()), - startPt + m2::PointD(r.minX(), r.minY()), - startPt + m2::PointD(r.maxX(), r.maxY()), - startPt + m2::PointD(r.minX(), r.maxY()) + m2::PointD(r.minX(), r.minY()), + m2::PointD(r.maxX(), r.minY()), + m2::PointD(r.maxX(), r.maxY()), + m2::PointD(r.minX(), r.minY()), + m2::PointD(r.maxX(), r.maxY()), + m2::PointD(r.minX(), r.maxY()) }; p->drawTrianglesList(pts, 6, p->skin()->mapColor(yg::Color(0, 0, 255, 255)), 0); @@ -1241,13 +1286,14 @@ namespace // UNIT_TEST_GL(TestDrawStringWithColor); // UNIT_TEST_GL(TestDrawUnicodeSymbols); // UNIT_TEST_GL(TestDrawTextRectWithFixedFont); +// UNIT_TEST_GL(TestDrawTextRect); // UNIT_TEST_GL(TestDrawStringOnString); // UNIT_TEST_GL(TestDrawTextOnPathInteractive); // UNIT_TEST_GL(TestDrawTextOnPathBigSymbols); -// UNIT_TEST_GL(TestDrawTextOnPath); -// UNIT_TEST_GL(TestDrawTextOnPathZigZag); -// UNIT_TEST_GL(TestDrawTextOnPathWithOffset); - UNIT_TEST_GL(TestDrawStraightTextElement); + UNIT_TEST_GL(TestDrawTextOnPath); + UNIT_TEST_GL(TestDrawTextOnPathZigZag); + UNIT_TEST_GL(TestDrawTextOnPathWithOffset); +// UNIT_TEST_GL(TestDrawStraightTextElement); UNIT_TEST_GL(TestDrawPathTextElement); // UNIT_TEST_GL(TestDrawTextOverflow); // UNIT_TEST_GL(TestDrawTextFiltering);