From a0d22c5a8f42ecb619378753ed68d6a2d2f3743c Mon Sep 17 00:00:00 2001 From: rachytski Date: Wed, 2 Feb 2011 23:25:30 +0200 Subject: [PATCH] added support for text colors. refactored text_renderer. --- indexer/drawing_rules.cpp | 2 + map/drawer_yg.cpp | 33 ++- map/information_display.cpp | 12 + map/render_queue_routine.cpp | 1 - yg/color.cpp | 5 + yg/color.hpp | 1 + yg/geometry_batcher.cpp | 385 ------------------------------- yg/geometry_batcher.hpp | 51 ---- yg/glyph_cache.cpp | 23 +- yg/glyph_cache.hpp | 4 +- yg/path_renderer.cpp | 255 ++++++++++++++++---- yg/path_renderer.hpp | 3 + yg/text_renderer.cpp | 260 ++++++++++++++++++++- yg/text_renderer.hpp | 98 ++++++-- yg/yg_tests/glyph_cache_test.cpp | 4 +- yg/yg_tests/screengl_test.cpp | 63 +++-- 16 files changed, 638 insertions(+), 562 deletions(-) diff --git a/indexer/drawing_rules.cpp b/indexer/drawing_rules.cpp index 3354f459c0..0286f3a24d 100644 --- a/indexer/drawing_rules.cpp +++ b/indexer/drawing_rules.cpp @@ -572,6 +572,7 @@ namespace drule { virtual void Write(FileWriterStream & ar) const { write_rules(ar, this); } virtual double GetTextHeight() const { return m_params.get<5>().m_v; } + virtual int GetColor() const { return m_params.get<6>().m_v; } static string arrKeys[9]; }; @@ -637,6 +638,7 @@ namespace drule { virtual void Write(FileWriterStream & ar) const { write_rules(ar, this); } virtual double GetTextHeight() const { return m_params.get<6>().m_v; } + virtual int GetColor() const {return m_params.get<7>().m_v;} static string arrKeys[10]; }; diff --git a/map/drawer_yg.cpp b/map/drawer_yg.cpp index 6b6602e8be..8ebde1b060 100644 --- a/map/drawer_yg.cpp +++ b/map/drawer_yg.cpp @@ -149,7 +149,7 @@ void DrawerYG::drawArea(vector const & pts, rule_ptr_t pRule, int de namespace { - double const min_text_height = 7.99; // 8 + double const min_text_height = 12; // 8 // double const min_text_height_mask = 9.99; // 10 } @@ -167,16 +167,39 @@ uint8_t DrawerYG::get_pathtext_font_size(rule_ptr_t pRule) const void DrawerYG::drawText(m2::PointD const & pt, string const & name, rule_ptr_t pRule, int depth) { - m_pScreen->drawText(pt, 0.0, get_text_font_size(pRule), name, depth); + yg::Color textColor(pRule->GetColor() == -1 ? yg::Color(0, 0, 0, 0) : yg::Color::fromXRGB(pRule->GetColor(), pRule->GetAlpha())); + if (textColor == yg::Color(255, 255, 255, 255)) + textColor = yg::Color(0, 0, 0, 0); + + m_pScreen->drawText( + pt, + 0.0, + get_text_font_size(pRule), + textColor, + name, + true, //pRule->GetOutlineColor() != -1, + yg::Color(255, 255, 255, 255), //yg::Color::fromXRGB(pRule->GetOutlineColor(), pRule->GetAlpha()), + depth, + false, + true); } bool DrawerYG::drawPathText(di::PathInfo const & info, string const & name, uint8_t fontSize, int /*depth*/) { // bool const isMasked = (double(fontSize) / m_visualScale >= min_text_height); - return m_pScreen->drawPathText( &info.m_path[0], info.m_path.size(), fontSize, name, - info.GetLength(), info.GetOffset(), - yg::gl::Screen::middle_line, true, yg::maxDepth); + return m_pScreen->drawPathText( &info.m_path[0], + info.m_path.size(), + fontSize, + yg::Color(0, 0, 0, 0), + name, + info.GetLength(), + info.GetOffset(), + yg::gl::Screen::middle_line, + true, + yg::Color(255, 255, 255, 255), + yg::maxDepth, + false); } shared_ptr DrawerYG::screen() const diff --git a/map/information_display.cpp b/map/information_display.cpp index 3e8c63d465..6e65abe110 100644 --- a/map/information_display.cpp +++ b/map/information_display.cpp @@ -249,7 +249,10 @@ void InformationDisplay::drawRuler(DrawerYG * pDrawer) pDrawer->screen()->drawText(scalerPts[1] + m2::PointD(7, -7), 0, 10, + yg::Color(0, 0, 0, 0), scalerText.c_str(), + true, + yg::Color(255, 255, 255, 255), yg::maxDepth, true, false); @@ -289,7 +292,10 @@ void InformationDisplay::drawCenter(DrawerYG * drawer) drawer->screen()->drawText( m2::PointD(m_displayRect.minX(), m_displayRect.minY()) + m2::PointD(10, m_isDebugInfoEnabled ? 40 : 20), 0, 10, + yg::Color(0, 0, 0, 0), out.str().c_str(), + true, + yg::Color(255, 255, 255, 255), yg::maxDepth, true, false); @@ -317,7 +323,10 @@ void InformationDisplay::drawDebugInfo(DrawerYG * drawer) drawer->screen()->drawText(m2::PointD(m_displayRect.minX(), m_displayRect.minY()) + m2::PointD(10, 20), 0, 10, + yg::Color(0, 0, 0, 0), out.str().c_str(), + true, + yg::Color(255, 255, 255, 255), yg::maxDepth, true, false); @@ -347,7 +356,10 @@ void InformationDisplay::drawMemoryWarning(DrawerYG * drawer) drawer->screen()->drawText(pos, 0, 10, + yg::Color(0, 0, 0, 0), out.str().c_str(), + true, + yg::Color(255, 255, 255, 255), yg::maxDepth, true, false); diff --git a/map/render_queue_routine.cpp b/map/render_queue_routine.cpp index 5e67b4d0f1..f480ce6770 100644 --- a/map/render_queue_routine.cpp +++ b/map/render_queue_routine.cpp @@ -218,7 +218,6 @@ void RenderQueueRoutine::Do() params.m_resourceManager = m_resourceManager; params.m_isMultiSampled = m_isMultiSampled; - params.m_useTextLayer = true; params.m_frameBuffer = m_frameBuffer; params.m_renderState = m_renderState; params.m_doPeriodicalUpdate = m_doPeriodicalUpdate; diff --git a/yg/color.cpp b/yg/color.cpp index 365abe29e1..e8fc73837c 100644 --- a/yg/color.cpp +++ b/yg/color.cpp @@ -62,4 +62,9 @@ namespace yg { return (l.r != r.r) || (l.g != r.g) || (l.b != r.b) || (l.a != r.a); } + + bool operator == (Color const & l, Color const & r) + { + return !(l != r); + } } diff --git a/yg/color.hpp b/yg/color.hpp index 29c0c78f69..850ce29262 100644 --- a/yg/color.hpp +++ b/yg/color.hpp @@ -22,6 +22,7 @@ namespace yg bool operator < (Color const & l, Color const & r); bool operator !=(Color const & l, Color const & r); + bool operator ==(Color const & l, Color const & r); inline int redFromARGB(uint32_t c) {return (c >> 16) & 0xFF;}; inline int greenFromARGB(uint32_t c) {return (c >> 8) & 0xFF;}; diff --git a/yg/geometry_batcher.cpp b/yg/geometry_batcher.cpp index 17a4914920..1299600856 100644 --- a/yg/geometry_batcher.cpp +++ b/yg/geometry_batcher.cpp @@ -11,7 +11,6 @@ #include "../coding/strutil.hpp" -#include "../geometry/angles.hpp" #include "../geometry/rect2d.hpp" #include "../base/assert.hpp" @@ -23,8 +22,6 @@ #include "../std/algorithm.hpp" #include "../std/bind.hpp" -#include "../3party/fribidi/lib/fribidi-deprecated.h" - #include "../base/start_mem_debug.hpp" namespace yg @@ -133,15 +130,6 @@ namespace yg base_t::endFrame(); } - wstring GeometryBatcher::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; - } bool GeometryBatcher::hasRoom(size_t verticesCount, size_t indicesCount, int pageID) const { @@ -251,155 +239,6 @@ namespace yg style->m_pageID); } - void GeometryBatcher::drawPath(m2::PointD const * points, size_t pointsCount, uint32_t styleID, double depth) - { -#ifdef PROFILER_YG - prof::block draw_path_block; -#endif - -//#ifdef DEBUG_DRAW_PATH -// LineStyle const * lineStyle = reinterpret_cast(styleID); -//#else - - ASSERT_GREATER_OR_EQUAL(pointsCount, 2, ()); - ResourceStyle const * style(m_skin->fromID(styleID)); - - ASSERT(style->m_cat == ResourceStyle::ELineStyle, ()); - LineStyle const * lineStyle = static_cast(style); -//#endif - - float rawTileStartLen = 0; - - for (size_t i = 0; i < pointsCount - 1; ++i) - { - m2::PointD dir = points[i + 1] - points[i]; - dir *= 1.0 / dir.Length(m2::PointD(0, 0)); - m2::PointD norm(-dir.y, dir.x); - - /// The length of the current segment. - float segLen = points[i + 1].Length(points[i]); - /// The remaining length of the segment - float segLenRemain = segLen; - - /// Geometry width. It's 1px wider than the pattern width. - int geomWidth = lineStyle->m_isSolid ? lineStyle->m_penInfo.m_w : lineStyle->m_penInfo.m_w + 4 - 2 * m_aaShift; - float geomHalfWidth = geomWidth / 2.0; - - /// Starting point of the tiles on this segment - m2::PointF rawTileStartPt = points[i]; - - /// Tiling procedes as following : - /// The leftmost tile goes antialiased at left and non-antialiased at right. - /// The inner tiles goes non-antialiased at both sides. - /// The rightmost tile goes non-antialised at left and antialiased at right side. - - /// Length of the actual pattern data being tiling(without antialiasing zones). - float rawTileLen = 0; - while (segLenRemain > 0) - { - rawTileLen = (lineStyle->m_isWrapped || lineStyle->m_isSolid) - ? segLen - : std::min(((float)lineStyle->rawTileLen() - rawTileStartLen), segLenRemain); - - float texMaxY = lineStyle->m_isSolid ? lineStyle->m_texRect.minY() + 1 : lineStyle->m_texRect.maxY() - m_aaShift; - float texMinY = lineStyle->m_isSolid ? lineStyle->m_texRect.minY() + 1 : lineStyle->m_texRect.minY() + m_aaShift; - - float texMinX = lineStyle->m_isSolid ? lineStyle->m_texRect.minX() + 1 : lineStyle->m_isWrapped ? 0 : lineStyle->m_texRect.minX() + 2 + rawTileStartLen; - float texMaxX = lineStyle->m_isSolid ? lineStyle->m_texRect.minX() + 1 : texMinX + rawTileLen; - - rawTileStartLen += rawTileLen; - if (rawTileStartLen >= lineStyle->rawTileLen()) - rawTileStartLen -= lineStyle->rawTileLen(); - ASSERT(rawTileStartLen < lineStyle->rawTileLen(), ()); - - m2::PointF rawTileEndPt(rawTileStartPt.x + dir.x * rawTileLen, rawTileStartPt.y + dir.y * rawTileLen); - - m2::PointF const fNorm = norm * geomHalfWidth; // enough to calc it once - m2::PointF coords[4] = - { - // vng: i think this "rawTileStartPt + fNorm" reading better, isn't it? - m2::PointF(rawTileStartPt.x + fNorm.x, rawTileStartPt.y + fNorm.y), - m2::PointF(rawTileStartPt.x - fNorm.x, rawTileStartPt.y - fNorm.y), - m2::PointF(rawTileEndPt.x - fNorm.x, rawTileEndPt.y - fNorm.y), - m2::PointF(rawTileEndPt.x + fNorm.x, rawTileEndPt.y + fNorm.y) - }; - - shared_ptr texture = m_skin->pages()[lineStyle->m_pageID]->texture(); - - m2::PointF texCoords[4] = - { - texture->mapPixel(m2::PointF(texMinX, texMinY)), - texture->mapPixel(m2::PointF(texMinX, texMaxY)), - texture->mapPixel(m2::PointF(texMaxX, texMaxY)), - texture->mapPixel(m2::PointF(texMaxX, texMinY)) - }; - - addTexturedFan(coords, texCoords, 4, depth, lineStyle->m_pageID); - - segLenRemain -= rawTileLen; - - rawTileStartPt = rawTileEndPt; - } - - bool isColorJoin = lineStyle->m_isSolid ? true : lineStyle->m_penInfo.atDashOffset(rawTileLen); - - /// Adding geometry for a line join between previous and current segment. - if ((i != pointsCount - 2) && (isColorJoin)) - { - m2::PointD nextDir = points[i + 2] - points[i + 1]; - nextDir *= 1.0 / nextDir.Length(m2::PointD(0, 0)); - m2::PointD nextNorm(-nextDir.y, nextDir.x); - - /// Computing the sin of angle between directions. - double alphaSin = dir.x * nextDir.y - dir.y * nextDir.x; - double alphaCos = dir.x * nextDir.x + dir.y * nextDir.y; - double alpha = atan2(alphaSin, alphaCos); - int angleSegCount = int(ceil(fabs(alpha) / (math::pi / 6))); - double angleStep = alpha / angleSegCount; - - m2::PointD startVec; - - if (alpha > 0) - { - /// The outer site is on the prevNorm direction. - startVec = -norm; - } - else - { - /// The outer site is on the -prevNorm direction - startVec = norm; - } - - shared_ptr texture = m_skin->pages()[lineStyle->m_pageID]->texture(); - - m2::PointF joinSegTex[3] = - { - texture->mapPixel(lineStyle->m_centerColorPixel), - texture->mapPixel(lineStyle->m_borderColorPixel), - texture->mapPixel(lineStyle->m_borderColorPixel) - }; - - m2::PointD prevStartVec = startVec; - for (int j = 0; j < angleSegCount; ++j) - { - /// Rotate start vector to find another point on a join. - startVec.Rotate(angleStep); - - /// Computing three points of a join segment. - m2::PointF joinSeg[3] = - { - m2::PointF(points[i + 1]), - m2::PointF(points[i + 1] + startVec * geomHalfWidth), - m2::PointF(points[i + 1] + prevStartVec * geomHalfWidth) - }; - - addTexturedFan(joinSeg, joinSegTex, 3, depth, lineStyle->m_pageID); - - prevStartVec = startVec; - } - } - } - } void GeometryBatcher::drawTrianglesList(m2::PointD const * points, size_t pointsCount, uint32_t styleID, double depth) { @@ -582,230 +421,6 @@ namespace yg m_pipelines[pageID].m_currentIndex += (size - 2) * 3; } - - void GeometryBatcher::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); - float y1 = ptGlyph.y - (p->m_yOffset - 1) - blOffset; - float y0 = y1 - (p->m_texRect.SizeY() - 2); - float x1 = x0 + (p->m_texRect.SizeX() - 2); - - - drawTexturedPolygon(ptOrg, angle, - p->m_texRect.minX() + 1, - p->m_texRect.minY() + 1, - p->m_texRect.maxX() - 1, - p->m_texRect.maxY() - 1, - x0, y0, x1, y1, - depth, - p->m_pageID); - } - - template - void GeometryBatcher::ForEachGlyph(uint8_t fontSize, wstring const & text, bool isMask, bool isFixedFont, ToDo toDo) - { - m2::PointD currPt(0, 0); - for (size_t i = 0; i < text.size(); ++i) - { - uint32_t glyphID = m_skin->mapGlyph(GlyphKey(text[i], fontSize, isMask), isFixedFont); - CharStyle const * p = static_cast(m_skin->fromID(glyphID)); - if (p) - { - toDo(currPt, p); - currPt += m2::PointD(p->m_xAdvance, 0); - } - } - } - - void GeometryBatcher::drawText(m2::PointD const & pt, float angle, uint8_t fontSize, string const & utf8Text, double depth, bool isFixedFont, bool log2vis) - { - wstring text = FromUtf8(utf8Text); - - if (log2vis) - text = Log2Vis(text); - - ForEachGlyph(fontSize, text, true, isFixedFont, bind(&GeometryBatcher::drawGlyph, this, cref(pt), _1, angle, 0, _2, depth)); - ForEachGlyph(fontSize, text, false, isFixedFont, bind(&GeometryBatcher::drawGlyph, this, cref(pt), _1, angle, 0, _2, depth)); - } - - m2::RectD const GeometryBatcher::textRect(string const & utf8Text, uint8_t fontSize, bool fixedFont, bool log2vis) - { - m2::RectD rect; - m2::PointD pt(0, 0); - - wstring text = FromUtf8(utf8Text); - if (log2vis) - text = Log2Vis(text); - - for (size_t i = 0; i < text.size(); ++i) - { - if (fixedFont) - { - uint32_t glyphID = m_skin->mapGlyph(GlyphKey(text[i], fontSize, false), fixedFont); - CharStyle const * p = static_cast(m_skin->fromID(glyphID)); - 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], fontSize, false)); - - 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; - } - - /// 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 GeometryBatcher::drawPathText( - m2::PointD const * path, size_t s, uint8_t fontSize, string const & utf8Text, - double fullLength, double pathOffset, TextPos pos, bool isMasked, double depth, bool isFixedFont) - { - if (isMasked) - { - if (!drawPathTextImpl(path, s, fontSize, utf8Text, fullLength, pathOffset, pos, true, depth, isFixedFont)) - return false; - } - return drawPathTextImpl(path, s, fontSize, utf8Text, fullLength, pathOffset, pos, false, depth, isFixedFont); - } - - bool GeometryBatcher::drawPathTextImpl( - m2::PointD const * path, size_t s, uint8_t fontSize, string const & utf8Text, - double fullLength, double pathOffset, TextPos pos, bool fromMask, double depth, bool isFixedFont) - { - pts_array arrPath(path, s, fullLength, pathOffset); - - wstring const text = Log2Vis(FromUtf8(utf8Text)); - - // calculate base line offset - float blOffset = 2; - switch (pos) - { - case under_line: blOffset -= fontSize; break; - case middle_line: blOffset -= fontSize / 2; break; - case above_line: blOffset -= 0; break; - } - - size_t const count = text.size(); - - vector glyphs(count); - - // get vector of glyphs and calculate string length - double strLength = 0.0; - for (size_t i = 0; i < count; ++i) - { - glyphs[i] = resourceManager()->getGlyphMetrics(GlyphKey(text[i], fontSize, fromMask)); - 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 = m_skin->mapGlyph(GlyphKey(text[i], fontSize, fromMask), isFixedFont); - CharStyle const * charStyle = static_cast(m_skin->fromID(glyphID)); - - drawGlyph(ptOrg, m2::PointD(0.0, 0.0), angle, blOffset, charStyle, depth); - - offset = glyphs[i].m_xAdvance; - } - - return true; - } - void GeometryBatcher::enableClipRect(bool flag) { flush(-1); diff --git a/yg/geometry_batcher.hpp b/yg/geometry_batcher.hpp index 7fee9e6580..0fc638964d 100644 --- a/yg/geometry_batcher.hpp +++ b/yg/geometry_batcher.hpp @@ -48,12 +48,8 @@ namespace yg typedef function onFlushFinishedFn; - enum TextPos { under_line, middle_line, above_line }; - private: - static wstring Log2Vis(wstring const & str); - typedef RenderStateUpdater base_t; shared_ptr m_skin; @@ -94,16 +90,6 @@ namespace yg int m_aaShift; - bool drawPathTextImpl(m2::PointD const * path, - size_t s, - uint8_t fontSize, - string const & utf8Text, - double fullLength, - double pathOffset, - TextPos pos, - bool fromMask, - double depth, - bool isFixedFont); public: @@ -138,36 +124,8 @@ namespace yg uint32_t styleID, double depth); - private: - template - void ForEachGlyph(uint8_t fontSize, wstring const & text, bool isMask, bool isFixedFont, ToDo toDo); public: - /// Drawing text from point rotated by the angle. - void drawText(m2::PointD const & pt, - float angle, - uint8_t fontSize, - string const & utf8Text, - double depth, - bool fixedFont = false, - bool log2vis = true); - - m2::RectD const textRect(string const & utf8Text, - uint8_t fontSize, - bool fixedFont = false, - bool log2vis = true); - - /// Drawing text in the middle of the path. - bool drawPathText(m2::PointD const * path, - size_t s, - uint8_t fontSize, - string const & utf8Text, - double fullLength, - double pathOffset, - TextPos pos, - bool isMasked, - double depth, - bool isFixedFont = false); /// This functions hide the base_t functions with the same name and signature /// to flush(-1) upon calling them @@ -195,15 +153,6 @@ namespace yg int aaShift() const; - private: - - void drawGlyph(m2::PointD const & ptOrg, - m2::PointD const & ptGlyph, - float angle, - float blOffset, - CharStyle const * p, - double depth); - /// drawing textured polygon with antialiasing /// we assume that the (tx0, ty0, tx1, ty1) area on texture is surrounded by (0, 0, 0, 0) pixels, /// and the 1px interior area is also (0, 0, 0, 0). diff --git a/yg/glyph_cache.cpp b/yg/glyph_cache.cpp index eed042f3bf..2164eeaad1 100644 --- a/yg/glyph_cache.cpp +++ b/yg/glyph_cache.cpp @@ -15,8 +15,8 @@ namespace gil = boost::gil; namespace yg { - GlyphKey::GlyphKey(int id, int fontSize, bool isMask) - : m_id(id), m_fontSize(fontSize), m_isMask(isMask) + GlyphKey::GlyphKey(int id, int fontSize, bool isMask, yg::Color const & color) + : m_id(id), m_fontSize(fontSize), m_isMask(isMask), m_color(color) {} uint32_t GlyphKey::toUInt32() const @@ -32,7 +32,9 @@ namespace yg return l.m_id < r.m_id; if (l.m_fontSize != r.m_fontSize) return l.m_fontSize < r.m_fontSize; - return l.m_isMask < r.m_isMask; + if (l.m_isMask != r.m_isMask) + return l.m_isMask < r.m_isMask; + return l.m_color < r.m_color; } GlyphCache::Params::Params(char const * blocksFile, char const * whiteListFile, char const * blackListFile, size_t maxSize) @@ -169,11 +171,6 @@ namespace yg 0 )); - /// glyph could appear in cache from getGlyphMetrics method, - /// so we should explicitly check this situation -// if (glyph.format == FT_GLYPH_FORMAT_OUTLINE) -// FTCHECK(FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1)); - FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph; shared_ptr info(new GlyphInfo()); @@ -184,7 +181,7 @@ namespace yg info->m_metrics.m_yOffset = bitmapGlyph ? bitmapGlyph->top - info->m_metrics.m_height : 0; info->m_metrics.m_xAdvance = bitmapGlyph ? int(bitmapGlyph->root.advance.x >> 16) : 0; info->m_metrics.m_yAdvance = bitmapGlyph ? int(bitmapGlyph->root.advance.y >> 16) : 0; - info->m_color = key.m_isMask ? yg::Color(255, 255, 255, 0) : yg::Color(0, 0, 0, 0); + info->m_color = key.m_color; if ((info->m_metrics.m_width != 0) && (info->m_metrics.m_height != 0)) { @@ -206,10 +203,10 @@ namespace yg DATA_TRAITS::pixel_t c; - gil::get_color(c, gil::red_t()) = key.m_isMask ? DATA_TRAITS::maxChannelVal : 0; - gil::get_color(c, gil::green_t()) = key.m_isMask ? DATA_TRAITS::maxChannelVal : 0; - gil::get_color(c, gil::blue_t()) = key.m_isMask ? DATA_TRAITS::maxChannelVal : 0; - gil::get_color(c, gil::alpha_t()) = 0; + gil::get_color(c, gil::red_t()) = key.m_color.r / DATA_TRAITS::channelScaleFactor; + gil::get_color(c, gil::green_t()) = key.m_color.g / DATA_TRAITS::channelScaleFactor; + gil::get_color(c, gil::blue_t()) = key.m_color.b / DATA_TRAITS::channelScaleFactor; + gil::get_color(c, gil::alpha_t()) = key.m_color.a / DATA_TRAITS::channelScaleFactor; for (size_t y = 0; y < srcView.height(); ++y) for (size_t x = 0; x < srcView.width(); ++x) diff --git a/yg/glyph_cache.hpp b/yg/glyph_cache.hpp index 2b0c7887fb..5f0b0c1155 100644 --- a/yg/glyph_cache.hpp +++ b/yg/glyph_cache.hpp @@ -32,8 +32,10 @@ namespace yg int m_id; int m_fontSize; bool m_isMask; + yg::Color m_color; + /// as it's used for fixed fonts only, the color doesn't matter uint32_t toUInt32() const; - GlyphKey(int id, int fontSize, bool isMask); + GlyphKey(int id, int fontSize, bool isMask, yg::Color const & color); }; struct Font; diff --git a/yg/path_renderer.cpp b/yg/path_renderer.cpp index c6827ae7d0..6a4b9297fa 100644 --- a/yg/path_renderer.cpp +++ b/yg/path_renderer.cpp @@ -14,69 +14,222 @@ namespace yg void PathRenderer::drawPath(m2::PointD const * points, size_t pointsCount, uint32_t styleID, double depth) { + #ifdef PROFILER_YG + prof::block draw_path_block; + #endif + + //#ifdef DEBUG_DRAW_PATH + // LineStyle const * lineStyle = reinterpret_cast(styleID); + //#else + + ASSERT_GREATER_OR_EQUAL(pointsCount, 2, ()); + ResourceStyle const * style(skin()->fromID(styleID)); + + ASSERT(style->m_cat == ResourceStyle::ELineStyle, ()); + LineStyle const * lineStyle = static_cast(style); + //#endif + + if (lineStyle->m_isSolid) + { + drawSolidPath(points, pointsCount, styleID, depth); + return; + } + + float rawTileStartLen = 0; + + for (size_t i = 0; i < pointsCount - 1; ++i) + { + m2::PointD dir = points[i + 1] - points[i]; + dir *= 1.0 / dir.Length(m2::PointD(0, 0)); + m2::PointD norm(-dir.y, dir.x); + + /// The length of the current segment. + float segLen = points[i + 1].Length(points[i]); + /// The remaining length of the segment + float segLenRemain = segLen; + + /// Geometry width. It's 1px wider than the pattern width. + int geomWidth = lineStyle->m_isSolid ? lineStyle->m_penInfo.m_w : lineStyle->m_penInfo.m_w + 4 - 2 * aaShift(); + float geomHalfWidth = geomWidth / 2.0; + + /// Starting point of the tiles on this segment + m2::PointF rawTileStartPt = points[i]; + + /// Tiling procedes as following : + /// The leftmost tile goes antialiased at left and non-antialiased at right. + /// The inner tiles goes non-antialiased at both sides. + /// The rightmost tile goes non-antialised at left and antialiased at right side. + + /// Length of the actual pattern data being tiling(without antialiasing zones). + float rawTileLen = 0; + while (segLenRemain > 0) + { + rawTileLen = (lineStyle->m_isWrapped || lineStyle->m_isSolid) + ? segLen + : std::min(((float)lineStyle->rawTileLen() - rawTileStartLen), segLenRemain); + + float texMaxY = lineStyle->m_isSolid ? lineStyle->m_texRect.minY() + 1 : lineStyle->m_texRect.maxY() - aaShift(); + float texMinY = lineStyle->m_isSolid ? lineStyle->m_texRect.minY() + 1 : lineStyle->m_texRect.minY() + aaShift(); + + float texMinX = lineStyle->m_isSolid ? lineStyle->m_texRect.minX() + 1 : lineStyle->m_isWrapped ? 0 : lineStyle->m_texRect.minX() + 2 + rawTileStartLen; + float texMaxX = lineStyle->m_isSolid ? lineStyle->m_texRect.minX() + 1 : texMinX + rawTileLen; + + rawTileStartLen += rawTileLen; + if (rawTileStartLen >= lineStyle->rawTileLen()) + rawTileStartLen -= lineStyle->rawTileLen(); + ASSERT(rawTileStartLen < lineStyle->rawTileLen(), ()); + + m2::PointF rawTileEndPt(rawTileStartPt.x + dir.x * rawTileLen, rawTileStartPt.y + dir.y * rawTileLen); + + m2::PointF const fNorm = norm * geomHalfWidth; // enough to calc it once + m2::PointF coords[4] = + { + // vng: i think this "rawTileStartPt + fNorm" reading better, isn't it? + m2::PointF(rawTileStartPt.x + fNorm.x, rawTileStartPt.y + fNorm.y), + m2::PointF(rawTileStartPt.x - fNorm.x, rawTileStartPt.y - fNorm.y), + m2::PointF(rawTileEndPt.x - fNorm.x, rawTileEndPt.y - fNorm.y), + m2::PointF(rawTileEndPt.x + fNorm.x, rawTileEndPt.y + fNorm.y) + }; + + shared_ptr texture = skin()->pages()[lineStyle->m_pageID]->texture(); + + m2::PointF texCoords[4] = + { + texture->mapPixel(m2::PointF(texMinX, texMinY)), + texture->mapPixel(m2::PointF(texMinX, texMaxY)), + texture->mapPixel(m2::PointF(texMaxX, texMaxY)), + texture->mapPixel(m2::PointF(texMaxX, texMinY)) + }; + + addTexturedFan(coords, texCoords, 4, depth, lineStyle->m_pageID); + + segLenRemain -= rawTileLen; + + rawTileStartPt = rawTileEndPt; + } + + bool isColorJoin = lineStyle->m_isSolid ? true : lineStyle->m_penInfo.atDashOffset(rawTileLen); + + /// Adding geometry for a line join between previous and current segment. + if ((i != pointsCount - 2) && (isColorJoin)) + { + m2::PointD nextDir = points[i + 2] - points[i + 1]; + nextDir *= 1.0 / nextDir.Length(m2::PointD(0, 0)); + m2::PointD nextNorm(-nextDir.y, nextDir.x); + + /// Computing the sin of angle between directions. + double alphaSin = dir.x * nextDir.y - dir.y * nextDir.x; + double alphaCos = dir.x * nextDir.x + dir.y * nextDir.y; + double alpha = atan2(alphaSin, alphaCos); + int angleSegCount = int(ceil(fabs(alpha) / (math::pi / 6))); + double angleStep = alpha / angleSegCount; + + m2::PointD startVec; + + if (alpha > 0) + { + /// The outer site is on the prevNorm direction. + startVec = -norm; + } + else + { + /// The outer site is on the -prevNorm direction + startVec = norm; + } + + shared_ptr texture = skin()->pages()[lineStyle->m_pageID]->texture(); + + m2::PointF joinSegTex[3] = + { + texture->mapPixel(lineStyle->m_centerColorPixel), + texture->mapPixel(lineStyle->m_borderColorPixel), + texture->mapPixel(lineStyle->m_borderColorPixel) + }; + + m2::PointD prevStartVec = startVec; + for (int j = 0; j < angleSegCount; ++j) + { + /// Rotate start vector to find another point on a join. + startVec.Rotate(angleStep); + + /// Computing three points of a join segment. + m2::PointF joinSeg[3] = + { + m2::PointF(points[i + 1]), + m2::PointF(points[i + 1] + startVec * geomHalfWidth), + m2::PointF(points[i + 1] + prevStartVec * geomHalfWidth) + }; + + addTexturedFan(joinSeg, joinSegTex, 3, depth, lineStyle->m_pageID); + + prevStartVec = startVec; + } + } + } + } + + void PathRenderer::drawSolidPath(m2::PointD const * points, size_t pointsCount, uint32_t styleID, double depth) + { ASSERT_GREATER_OR_EQUAL(pointsCount, 2, ()); ResourceStyle const * style(skin()->fromID(styleID)); ASSERT(style->m_cat == ResourceStyle::ELineStyle, ()); LineStyle const * lineStyle = static_cast(style); - if (lineStyle->m_isSolid) + ASSERT(lineStyle->m_isSolid, ()); + + for (size_t i = 0; i < pointsCount - 1; ++i) { - for (size_t i = 0; i < pointsCount - 1; ++i) + m2::PointD dir = points[i + 1] - points[i]; + double len = dir.Length(m2::PointD(0, 0)); + dir *= 1.0 / len; + m2::PointD norm(-dir.y, dir.x); + m2::PointD const & nextPt = points[i + 1]; + + float geomHalfWidth = (lineStyle->m_penInfo.m_w + 4 - aaShift() * 2) / 2.0; + + float texMinX = lineStyle->m_texRect.minX() + 1; + float texMaxX = lineStyle->m_texRect.maxX() - 1; + + float texMinY = lineStyle->m_texRect.maxY() - aaShift(); + float texMaxY = lineStyle->m_texRect.minY() + aaShift(); + + float texCenterX = (texMinX + texMaxX) / 2; + + m2::PointF const fNorm = norm * geomHalfWidth; // enough to calc it once + m2::PointF const fDir(fNorm.y, -fNorm.x); + + m2::PointF coords[8] = { - m2::PointD dir = points[i + 1] - points[i]; - double len = dir.Length(m2::PointD(0, 0)); - dir *= 1.0 / len; - m2::PointD norm(-dir.y, dir.x); - m2::PointD const & nextPt = points[i + 1]; + /// left round cap + points[i] - fDir + fNorm, + points[i] - fDir - fNorm, + points[i] + fNorm, + /// inner part + points[i] - fNorm, + nextPt + fNorm, + nextPt - fNorm, + /// right round cap + nextPt + fDir + fNorm, + nextPt + fDir - fNorm + }; - float geomHalfWidth = (lineStyle->m_penInfo.m_w + 4 - aaShift() * 2) / 2.0; + shared_ptr texture = skin()->pages()[lineStyle->m_pageID]->texture(); - float texMinX = lineStyle->m_texRect.minX() + 1; - float texMaxX = lineStyle->m_texRect.maxX() - 1; + m2::PointF texCoords[8] = + { + texture->mapPixel(m2::PointF(texMinX, texMinY)), + texture->mapPixel(m2::PointF(texMinX, texMaxY)), + texture->mapPixel(m2::PointF(texCenterX, texMinY)), + texture->mapPixel(m2::PointF(texCenterX, texMaxY)), + texture->mapPixel(m2::PointF(texCenterX, texMinY)), + texture->mapPixel(m2::PointF(texCenterX, texMaxY)), + texture->mapPixel(m2::PointF(texMaxX, texMinY)), + texture->mapPixel(m2::PointF(texMaxX, texMaxY)) + }; - float texMinY = lineStyle->m_texRect.maxY() - aaShift(); - float texMaxY = lineStyle->m_texRect.minY() + aaShift(); - - float texCenterX = (texMinX + texMaxX) / 2; - - m2::PointF const fNorm = norm * geomHalfWidth; // enough to calc it once - m2::PointF const fDir(fNorm.y, -fNorm.x); - - m2::PointF coords[8] = - { - /// left round cap - points[i] - fDir + fNorm, - points[i] - fDir - fNorm, - points[i] + fNorm, - /// inner part - points[i] - fNorm, - nextPt + fNorm, - nextPt - fNorm, - /// right round cap - nextPt + fDir + fNorm, - nextPt + fDir - fNorm - }; - - shared_ptr texture = skin()->pages()[lineStyle->m_pageID]->texture(); - - m2::PointF texCoords[8] = - { - texture->mapPixel(m2::PointF(texMinX, texMinY)), - texture->mapPixel(m2::PointF(texMinX, texMaxY)), - texture->mapPixel(m2::PointF(texCenterX, texMinY)), - texture->mapPixel(m2::PointF(texCenterX, texMaxY)), - texture->mapPixel(m2::PointF(texCenterX, texMinY)), - texture->mapPixel(m2::PointF(texCenterX, texMaxY)), - texture->mapPixel(m2::PointF(texMaxX, texMinY)), - texture->mapPixel(m2::PointF(texMaxX, texMaxY)) - }; - - addTexturedStrip(coords, texCoords, 8, depth, lineStyle->m_pageID); - } + addTexturedStrip(coords, texCoords, 8, depth, lineStyle->m_pageID); } - else - base_t::drawPath(points, pointsCount, styleID, depth); } } } diff --git a/yg/path_renderer.hpp b/yg/path_renderer.hpp index da9bed1ec1..bfff514045 100644 --- a/yg/path_renderer.hpp +++ b/yg/path_renderer.hpp @@ -10,6 +10,9 @@ namespace yg class PathRenderer : public GeometryBatcher { private: + + void drawSolidPath(m2::PointD const * points, size_t pointsCount, uint32_t styleID, double depth); + public: typedef GeometryBatcher base_t; diff --git a/yg/text_renderer.cpp b/yg/text_renderer.cpp index 8f9bde194e..b984798103 100644 --- a/yg/text_renderer.cpp +++ b/yg/text_renderer.cpp @@ -9,8 +9,12 @@ #include "../coding/strutil.hpp" +#include "../geometry/angles.hpp" + #include "../std/bind.hpp" +#include "../3party/fribidi/lib/fribidi-deprecated.h" + #include "../base/start_mem_debug.hpp" @@ -18,37 +22,36 @@ namespace yg { namespace gl { - TextRenderer::Params::Params() : m_useTextLayer(false) - { - } - - TextRenderer::TextRenderer(Params const & params) - : base_t(params), m_useTextLayer(params.m_useTextLayer) + TextRenderer::TextRenderer(base_t::Params const & params) + : base_t(params) {} - void TextRenderer::TextObj::Draw(GeometryBatcher * pBatcher) const + void TextRenderer::TextObj::Draw(TextRenderer * pTextRenderer) const { - pBatcher->drawText(m_pt, 0.0, m_size, m_utf8Text, yg::maxDepth, m_isFixedFont, m_log2vis); + pTextRenderer->drawTextImpl(m_pt, 0.0, m_size, m_color, m_utf8Text, m_isMasked, m_maskColor, yg::maxDepth, m_isFixedFont, m_log2vis); } - m2::RectD const TextRenderer::TextObj::GetLimitRect(GeometryBatcher * pBatcher) const + m2::RectD const TextRenderer::TextObj::GetLimitRect(TextRenderer* pTextRenderer) const { - return m2::Offset(pBatcher->textRect(m_utf8Text, m_size, false, m_log2vis), m_pt); + return m2::Offset(pTextRenderer->textRect(m_utf8Text, m_size, false, m_log2vis), m_pt); } void TextRenderer::drawText(m2::PointD const & pt, float angle, uint8_t fontSize, + yg::Color const & color, string const & utf8Text, + bool isMasked, + yg::Color const & maskColor, double depth, bool isFixedFont, bool log2vis) { if (isFixedFont) - base_t::drawText(pt, angle, fontSize, utf8Text, depth, isFixedFont, log2vis); + drawTextImpl(pt, angle, fontSize, color, utf8Text, isMasked, maskColor, depth, isFixedFont, log2vis); else { - TextObj obj(pt, utf8Text, fontSize, depth, isFixedFont, log2vis); + TextObj obj(pt, utf8Text, fontSize, color, isMasked, maskColor, depth, isFixedFont, log2vis); m2::RectD r = obj.GetLimitRect(this); /* m2::PointD pts[5] = @@ -81,5 +84,238 @@ namespace yg base_t::endFrame(); } + + template + void TextRenderer::ForEachGlyph(uint8_t fontSize, yg::Color const & color, wstring const & text, bool isMasked, bool isFixedFont, ToDo toDo) + { + m2::PointD currPt(0, 0); + for (size_t i = 0; i < text.size(); ++i) + { + uint32_t glyphID = skin()->mapGlyph(GlyphKey(text[i], fontSize, isMasked, color), isFixedFont); + 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(m2::PointD const & pt, float angle, uint8_t fontSize, yg::Color const & color, string const & utf8Text, bool isMasked, yg::Color const & maskColor, double depth, bool isFixedFont, bool log2vis) + { + wstring text = FromUtf8(utf8Text); + + if (log2vis) + text = Log2Vis(text); + + if (isMasked) + ForEachGlyph(fontSize, maskColor, text, true, isFixedFont, bind(&TextRenderer::drawGlyph, this, cref(pt), _1, angle, 0, _2, depth)); + ForEachGlyph(fontSize, color, text, false, isFixedFont, bind(&TextRenderer::drawGlyph, this, cref(pt), _1, angle, 0, _2, depth)); + } + + m2::RectD const TextRenderer::textRect(string const & utf8Text, uint8_t fontSize, bool fixedFont, bool log2vis) + { + m2::RectD rect; + m2::PointD pt(0, 0); + + wstring text = FromUtf8(utf8Text); + if (log2vis) + text = Log2Vis(text); + + for (size_t i = 0; i < text.size(); ++i) + { + if (fixedFont) + { + uint32_t glyphID = skin()->mapGlyph(GlyphKey(text[i], fontSize, false, yg::Color(0, 0, 0, 0)), fixedFont); + CharStyle const * p = static_cast(skin()->fromID(glyphID)); + 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], fontSize, false, 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; + } + + /// 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( + m2::PointD const * path, size_t s, uint8_t fontSize, yg::Color const & color, string const & utf8Text, + double fullLength, double pathOffset, TextPos pos, bool isMasked, yg::Color const & maskColor, double depth, bool isFixedFont) + { + if (isMasked) + if (!drawPathTextImpl(path, s, fontSize, maskColor, utf8Text, fullLength, pathOffset, pos, true, depth, isFixedFont)) + return false; + return drawPathTextImpl(path, s, fontSize, color, utf8Text, fullLength, pathOffset, pos, false, depth, isFixedFont); + } + + bool TextRenderer::drawPathTextImpl( + m2::PointD const * path, size_t s, uint8_t fontSize, yg::Color const & color, string const & utf8Text, + double fullLength, double pathOffset, TextPos pos, bool isMasked, double depth, bool isFixedFont) + { + pts_array arrPath(path, s, fullLength, pathOffset); + + wstring const text = Log2Vis(FromUtf8(utf8Text)); + + // calculate base line offset + float blOffset = 2; + switch (pos) + { + case under_line: blOffset -= fontSize; break; + case middle_line: blOffset -= fontSize / 2; break; + case above_line: blOffset -= 0; break; + } + + size_t const count = text.size(); + + vector glyphs(count); + + // get vector of glyphs and calculate string length + double strLength = 0.0; + for (size_t i = 0; i < count; ++i) + { + glyphs[i] = resourceManager()->getGlyphMetrics(GlyphKey(text[i], fontSize, 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], fontSize, isMasked, color), isFixedFont); + 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; + } + + 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); + float y1 = ptGlyph.y - (p->m_yOffset - 1) - blOffset; + float y0 = y1 - (p->m_texRect.SizeY() - 2); + float x1 = x0 + (p->m_texRect.SizeX() - 2); + + + drawTexturedPolygon(ptOrg, angle, + p->m_texRect.minX() + 1, + p->m_texRect.minY() + 1, + p->m_texRect.maxX() - 1, + p->m_texRect.maxY() - 1, + x0, y0, x1, y1, + depth, + p->m_pageID); + } + } } diff --git a/yg/text_renderer.hpp b/yg/text_renderer.hpp index 6fad4b4619..d0554eb687 100644 --- a/yg/text_renderer.hpp +++ b/yg/text_renderer.hpp @@ -15,23 +15,32 @@ namespace yg class TextRenderer : public PathRenderer { + public: + + enum TextPos { under_line, middle_line, above_line }; + + private: + class TextObj { m2::PointD m_pt; uint8_t m_size; string m_utf8Text; + bool m_isMasked; double m_depth; bool m_isFixedFont; bool m_log2vis; + yg::Color m_color; + yg::Color m_maskColor; public: - TextObj(m2::PointD const & pt, string const & txt, uint8_t sz, double d, bool isFixedFont, bool log2vis) - : m_pt(pt), m_size(sz), m_utf8Text(txt), m_depth(d), m_isFixedFont(isFixedFont), m_log2vis(log2vis) + TextObj(m2::PointD const & pt, string const & txt, uint8_t sz, yg::Color const & c, bool isMasked, yg::Color const & maskColor, double d, bool isFixedFont, bool log2vis) + : m_pt(pt), m_size(sz), m_utf8Text(txt), m_isMasked(isMasked), m_depth(d), m_isFixedFont(isFixedFont), m_log2vis(log2vis), m_color(c), m_maskColor(maskColor) { } - void Draw(GeometryBatcher * pBatcher) const; - m2::RectD const GetLimitRect(GeometryBatcher * pBatcher) const; + void Draw(TextRenderer * pTextRenderer) const; + m2::RectD const GetLimitRect(TextRenderer * pTextRenderer) const; struct better_depth { @@ -44,26 +53,81 @@ namespace yg m4::Tree m_tree; - bool m_useTextLayer; + static wstring Log2Vis(wstring const & str); + + template + void ForEachGlyph(uint8_t fontSize, yg::Color const & color, wstring const & text, bool isMask, bool isFixedFont, ToDo toDo); + + void drawGlyph(m2::PointD const & ptOrg, + m2::PointD const & ptGlyph, + float angle, + float blOffset, + CharStyle const * p, + double depth); + + + bool drawPathTextImpl(m2::PointD const * path, + size_t s, + uint8_t fontSize, + yg::Color const & color, + string const & utf8Text, + double fullLength, + double pathOffset, + TextPos pos, + bool isMasked, + double depth, + bool isFixedFont); + + /// Drawing text from point rotated by the angle. + void drawTextImpl(m2::PointD const & pt, + float angle, + uint8_t fontSize, + yg::Color const & color, + string const & utf8Text, + bool isMasked, + yg::Color const & maskColor, + double depth, + bool fixedFont, + bool log2vis); + public: typedef PathRenderer base_t; - struct Params : base_t::Params - { - bool m_useTextLayer; - Params(); - }; - TextRenderer(Params const & params); + TextRenderer(base_t::Params const & params); + /// Drawing text from point rotated by the angle. void drawText(m2::PointD const & pt, - float angle, - uint8_t fontSize, - string const & utf8Text, - double depth, - bool isFixedFont = false, - bool log2vis = false); + float angle, + uint8_t fontSize, + yg::Color const & color, + string const & utf8Text, + bool isMasked, + yg::Color const & maskColor, + double depth, + bool fixedFont, + bool log2vis); + + m2::RectD const textRect(string const & utf8Text, + uint8_t fontSize, + bool fixedFont, + bool log2vis); + + /// Drawing text in the middle of the path. + bool drawPathText(m2::PointD const * path, + size_t s, + uint8_t fontSize, + yg::Color const & color, + string const & utf8Text, + double fullLength, + double pathOffset, + TextPos pos, + bool isMasked, + yg::Color const & maskColor, + double depth, + bool isFixedFont = false); + void setClipRect(m2::RectI const & rect); diff --git a/yg/yg_tests/glyph_cache_test.cpp b/yg/yg_tests/glyph_cache_test.cpp index 1dea01a9ba..f23fd8428e 100644 --- a/yg/yg_tests/glyph_cache_test.cpp +++ b/yg/yg_tests/glyph_cache_test.cpp @@ -7,8 +7,8 @@ UNIT_TEST(GlyphCacheTest_Main) { yg::GlyphCache cache(yg::GlyphCache::Params("", "", "", 200000)); cache.addFont(GetPlatform().ReadPathForFile("dejavusans.ttf").c_str()); - shared_ptr g1 = cache.getGlyph(yg::GlyphKey('#', 40, true)); + shared_ptr g1 = cache.getGlyph(yg::GlyphKey('#', 40, true, yg::Color(255, 255, 255, 255))); // g1->dump(GetPlatform().WritablePathForFile("#_mask.png").c_str()); - shared_ptr g2 = cache.getGlyph(yg::GlyphKey('#', 40, false)); + shared_ptr g2 = cache.getGlyph(yg::GlyphKey('#', 40, false, yg::Color(0, 0, 0, 0))); // g2->dump(GetPlatform().WritablePathForFile("#.png").c_str()); } diff --git a/yg/yg_tests/screengl_test.cpp b/yg/yg_tests/screengl_test.cpp index 573984dc33..aea95f3fce 100644 --- a/yg/yg_tests/screengl_test.cpp +++ b/yg/yg_tests/screengl_test.cpp @@ -520,12 +520,12 @@ namespace double pat[2] = {2, 2}; p->drawPath(path, sizeof(path) / sizeof(m2::PointD), p->skin()->mapPenInfo(yg::PenInfo(yg::Color(0, 0, 0, 0xFF), 2, pat, 2, 0)), 0); - p->drawText(m2::PointD(200, 200), 0 , 20, "0", 0); - p->drawText(m2::PointD(240, 200), math::pi / 2 , 20, "0", 0); - p->drawText(m2::PointD(280, 200), math::pi , 20, "0", 0); - p->drawText(m2::PointD(320, 200), math::pi * 3 / 2 , 20, "0", 0); - p->drawText(m2::PointD(360, 200), math::pi / 18, 20, "0", 0); - p->drawText(m2::PointD(40, 50), math::pi / 18, 20, "Simplicity is the ultimate sophistication", 0); + p->drawText(m2::PointD(200, 200), 0 , 20, yg::Color(0, 0, 0, 0), "0", true, yg::Color(255, 255, 255, 255), 0, false, true); + p->drawText(m2::PointD(240, 200), math::pi / 2 , 20, yg::Color(0, 0, 0, 0), "0", true, yg::Color(255, 255, 255, 255), 0, false, true); + p->drawText(m2::PointD(280, 200), math::pi , 20, yg::Color(0, 0, 0, 0), "0", true, yg::Color(255, 255, 255, 255), 0, false, true); + p->drawText(m2::PointD(320, 200), math::pi * 3 / 2 , 20, yg::Color(0, 0, 0, 0), "0", true, yg::Color(255, 255, 255, 255), 0, false, true); + p->drawText(m2::PointD(360, 200), math::pi / 18, 20, yg::Color(0, 0, 0, 0), "0", true, yg::Color(255, 255, 255, 255), 0, false, true); + p->drawText(m2::PointD(40, 50), math::pi / 18, 20, yg::Color(0, 0, 0, 0), "Simplicity is the ultimate sophistication", true, yg::Color(255, 255, 255, 255), 0, false, true); } }; @@ -533,7 +533,7 @@ namespace { void DoDraw(shared_ptr p) { - p->drawText(m2::PointD(40, 50), 0, 20, "X", 1); + p->drawText(m2::PointD(40, 50), 0, 20, yg::Color(0, 0, 0, 0), "X", true, yg::Color(255, 255, 255, 255), 1, false, true); } }; @@ -541,7 +541,7 @@ namespace { void DoDraw(shared_ptr p) { - p->drawText(m2::PointD(40, 50), 0, 20, " ", 1); + p->drawText(m2::PointD(40, 50), 0, 20, yg::Color(0, 0, 0, 0), " ", true, yg::Color(255, 255, 255, 255), 1, false, true); } }; @@ -554,7 +554,7 @@ namespace for (size_t i = 0; i < maxTimes; ++i) for (size_t j = 1; j <= i+1; ++j) - p->drawText(m2::PointD(40, 10 + yStep * i), math::pi / 6, 20, "Simplicity is the ultimate sophistication", 0); + p->drawText(m2::PointD(40, 10 + yStep * i), math::pi / 6, 20, yg::Color(0, 0, 0, 0), "Simplicity is the ultimate sophistication", true, yg::Color(255, 255, 255, 255), 0, false, true); } }; @@ -570,7 +570,7 @@ namespace yg::PenInfo penInfo = yg::PenInfo(yg::Color(0, 0, 0, 0xFF), 2, &pat[0], ARRAY_SIZE(pat), 0); yg::PenInfo solidPenInfo = yg::PenInfo(yg::Color(0xFF, 0, 0, 0xFF), 4, 0, 0, 0); - p->drawText(m2::PointD(40, 50), 0, 20, "S", 0); + p->drawText(m2::PointD(40, 50), 0, 20, yg::Color(0, 0, 0, 0), "S", true, yg::Color(255, 255, 255, 255), 0, false, true); p->drawPath(&path[0], path.size(), p->skin()->mapPenInfo(solidPenInfo), 0); } @@ -580,7 +580,7 @@ namespace { void DoDraw(shared_ptr p) { - p->drawText(m2::PointD(40, 50), 0/*, math::pi / 18*/, 20, "Simplicity is the ultimate sophistication", 0); + p->drawText(m2::PointD(40, 50), 0/*, math::pi / 18*/, 20, yg::Color(0, 0, 0, 0), "Simplicity is the ultimate sophistication", true, yg::Color(255, 255, 255, 255), 0, false, true); } }; @@ -588,16 +588,25 @@ namespace { void DoDraw(shared_ptr p) { - p->drawText(m2::PointD(40, 50), 0/*, math::pi / 18*/, 12, "Simplicity is the ultimate sophistication", 0, true); + p->drawText(m2::PointD(40, 50), 0/*, math::pi / 18*/, 12, yg::Color(0, 0, 0, 0), "Simplicity is the ultimate sophistication", true, yg::Color(255, 255, 255, 255), 0, true, true); } }; + struct TestDrawStringWithColor + { + void DoDraw(shared_ptr p) + { + p->drawText(m2::PointD(40, 50), 0/*, math::pi / 18*/, 25, yg::Color(0, 0, 255, 255), "Simplicity is the ultimate sophistication", true, yg::Color(255, 255, 255, 255), 0, false, true); + } + }; + + struct TestDrawUnicodeSymbols { void DoDraw(shared_ptr p) { - p->drawText(m2::PointD(40, 50), 0, 12, "Latin Symbol : A", 0); - p->drawText(m2::PointD(40, 80), 0, 12, "Cyrillic Symbol : Ы", 0); + p->drawText(m2::PointD(40, 50), 0, 12, yg::Color(0, 0, 0, 0), "Latin Symbol : A", true, yg::Color(255, 255, 255, 255), 0, false, true); + p->drawText(m2::PointD(40, 80), 0, 12, yg::Color(0, 0, 0, 0), "Cyrillic Symbol : Ы", true, yg::Color(255, 255, 255, 255), 0, false, true); } }; @@ -655,7 +664,7 @@ namespace void DoDraw(shared_ptr p) { p->drawPath(&m_path[0], m_path.size(), p->skin()->mapPenInfo(m_penInfo), 0); - p->drawPathText(&m_path[0], m_path.size(), 10, m_text, calc_length(m_path), 0.0, yg::gl::Screen::middle_line, true, 0); + p->drawPathText(&m_path[0], m_path.size(), 10, yg::Color(0, 0, 0, 0), m_text, calc_length(m_path), 0.0, yg::gl::Screen::middle_line, true, yg::Color(255, 255, 255, 255), 0, false); } }; @@ -666,8 +675,8 @@ namespace p->drawPath(&m_path[0], m_path.size(), p->skin()->mapPenInfo(m_penInfo), 0); double const len = calc_length(m_path); - p->drawPathText(&m_path[0], m_path.size(), 10, m_text, len, 0.0, yg::gl::Screen::above_line, true, 0); - p->drawPathText(&m_path[0], m_path.size(), 10, m_text, len, 0.0, yg::gl::Screen::under_line, true, 0); + p->drawPathText(&m_path[0], m_path.size(), 10, yg::Color(0, 0, 0, 0), m_text, len, 0.0, yg::gl::Screen::above_line, true, yg::Color(255, 255, 255, 255), 0, false); + p->drawPathText(&m_path[0], m_path.size(), 10, yg::Color(0, 0, 0, 0), m_text, len, 0.0, yg::gl::Screen::under_line, true, yg::Color(255, 255, 255, 255), 0, false); } }; @@ -681,7 +690,7 @@ namespace int startY = 30; for (size_t i = 0; i < sizesCount; ++i) { - p->drawText(m2::PointD(10, startY), 0, startSize + i, "Simplicity is the ultimate sophistication. Leonardo Da Vinci", 0); + p->drawText(m2::PointD(10, startY), 0, startSize + i, yg::Color(0, 0, 0, 0), "Simplicity is the ultimate sophistication. Leonardo Da Vinci", true, yg::Color(255, 255, 255, 255), 0, false, true); startY += startSize + i; } } @@ -697,8 +706,8 @@ namespace int startY = 30; for (size_t i = 0; i < sizesCount; ++i) { - p->drawText(m2::PointD(10, startY), 0, startSize/* + i*/, "Simplicity is the ultimate sophistication. Leonardo Da Vinci", 100); - p->drawText(m2::PointD(5, startY + (startSize + i) / 2), 0, startSize/* + i*/, "This text should be filtered", 100); + p->drawText(m2::PointD(10, startY), 0, startSize/* + i*/, yg::Color(0, 0, 0, 0), "Simplicity is the ultimate sophistication. Leonardo Da Vinci", true, yg::Color(255, 255, 255, 255), 100, false, true); + p->drawText(m2::PointD(5, startY + (startSize + i) / 2), 0, startSize/* + i*/, yg::Color(0, 0, 0, 0), "This text should be filtered", true, yg::Color(255, 255, 255, 255), 100, false, true); startY += startSize + i; } } @@ -720,8 +729,13 @@ namespace m2::PointD(rand() % 500, rand() % 500), 0, rand() % (endSize - startSize) + startSize, + yg::Color(rand() % 255, rand() % 255, rand() % 255, 255), texts[rand() % (sizeof(texts) / sizeof(char*))], - rand() % 10); + true, + yg::Color(255, 255, 255, 255), + rand() % 10, + false, + true); } }; @@ -910,14 +924,15 @@ namespace // UNIT_TEST_GL(TestDrawSingleSymbolAndSolidPath); // UNIT_TEST_GL(TestDrawString); // UNIT_TEST_GL(TestDrawStringWithFixedFont); - UNIT_TEST_GL(TestDrawUnicodeSymbols); - UNIT_TEST_GL(TestDrawTextRectWithFixedFont); + UNIT_TEST_GL(TestDrawStringWithColor); +// UNIT_TEST_GL(TestDrawUnicodeSymbols); +// UNIT_TEST_GL(TestDrawTextRectWithFixedFont); // UNIT_TEST_GL(TestDrawStringOnString); // UNIT_TEST_GL(TestDrawTextOnPath); // UNIT_TEST_GL(TestDrawTextOnPathWithOffset); // UNIT_TEST_GL(TestDrawTextOverflow); // UNIT_TEST_GL(TestDrawTextFiltering); -// UNIT_TEST_GL(TestDrawRandomTextFiltering); + UNIT_TEST_GL(TestDrawRandomTextFiltering); // UNIT_TEST_GL(TestDrawSGIConvex); // UNIT_TEST_GL(TestDrawPoly); // UNIT_TEST_GL(TestDrawSolidRect);