From ea5f105f55eece846c65ab25065ec0650f6ba3e5 Mon Sep 17 00:00:00 2001 From: ExMix Date: Sun, 10 Aug 2014 20:25:37 +0300 Subject: [PATCH] [drape] Text shape refactoring. Move placing algos into TextLayout class. --- drape/attribute_buffer_mutator.cpp | 16 + drape/attribute_buffer_mutator.hpp | 7 + drape_frontend/apply_feature_functors.cpp | 2 +- drape_frontend/drape_frontend.pro | 3 + drape_frontend/intrusive_vector.hpp | 54 +++ drape_frontend/path_text_shape.cpp | 520 ++++++++++------------ drape_frontend/path_text_shape.hpp | 47 +- drape_frontend/text_layout.cpp | 315 +++++++++++++ drape_frontend/text_layout.hpp | 85 ++++ drape_frontend/text_shape.cpp | 337 +++++--------- drape_frontend/text_shape.hpp | 22 +- 11 files changed, 833 insertions(+), 575 deletions(-) create mode 100644 drape_frontend/intrusive_vector.hpp create mode 100644 drape_frontend/text_layout.cpp create mode 100644 drape_frontend/text_layout.hpp diff --git a/drape/attribute_buffer_mutator.cpp b/drape/attribute_buffer_mutator.cpp index 465a25b78a..4554482385 100644 --- a/drape/attribute_buffer_mutator.cpp +++ b/drape/attribute_buffer_mutator.cpp @@ -3,9 +3,25 @@ namespace dp { +AttributeBufferMutator::~AttributeBufferMutator() +{ + SharedBufferManager & mng = SharedBufferManager::instance(); + for (size_t i = 0; i < m_array.size(); ++i) + { + TBufferNode & node = m_array[i]; + mng.freeSharedBuffer(node.second, node.first); + } +} + void AttributeBufferMutator::AddMutation(BindingInfo const & info, MutateNode const & node) { m_data[info].push_back(node); } +void * AttributeBufferMutator::AllocateMutationBuffer(uint32_t byteCount) +{ + m_array.push_back(make_pair(SharedBufferManager::instance().reserveSharedBuffer(byteCount), byteCount)); + return &((*m_array.back().first)[0]); +} + } // namespace dp diff --git a/drape/attribute_buffer_mutator.hpp b/drape/attribute_buffer_mutator.hpp index 150defcd64..dfc77359e2 100644 --- a/drape/attribute_buffer_mutator.hpp +++ b/drape/attribute_buffer_mutator.hpp @@ -3,6 +3,8 @@ #include "pointers.hpp" #include "binding_info.hpp" +#include "../base/shared_buffer_manager.hpp" + #include "../std/stdint.hpp" #include "../std/map.hpp" @@ -26,10 +28,14 @@ struct MutateNode class AttributeBufferMutator { + typedef pair TBufferNode; + typedef vector TBufferArray; typedef vector TMutateNodes; typedef map TMutateData; public: + ~AttributeBufferMutator(); void AddMutation(BindingInfo const & info, MutateNode const & node); + void * AllocateMutationBuffer(uint32_t byteCount); private: friend class VertexArrayBuffer; @@ -37,6 +43,7 @@ private: private: TMutateData m_data; + TBufferArray m_array; }; } // namespace dp diff --git a/drape_frontend/apply_feature_functors.cpp b/drape_frontend/apply_feature_functors.cpp index 11cb6e3584..90eadde06c 100644 --- a/drape_frontend/apply_feature_functors.cpp +++ b/drape_frontend/apply_feature_functors.cpp @@ -312,7 +312,7 @@ void ApplyLineFeature::ProcessRule(Stylist::rule_wrapper_t const & rule) params.m_text = m_captions.GetPathName(); params.m_textFont = fontDecl; - m_context.InsertShape(m_tileKey, dp::MovePointer(new PathTextShape(m_spline, params))); + m_context.InsertShape(m_tileKey, dp::MovePointer(new PathTextShape(m_spline, params, m_currentScaleGtoP))); } if (pLineRule != NULL) diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro index 3c68f6d5a1..2d0b842ef8 100644 --- a/drape_frontend/drape_frontend.pro +++ b/drape_frontend/drape_frontend.pro @@ -38,6 +38,7 @@ SOURCES += \ path_text_shape.cpp \ path_symbol_shape.cpp \ common_structures.cpp \ + text_layout.cpp HEADERS += \ engine_context.hpp \ @@ -72,3 +73,5 @@ HEADERS += \ path_symbol_shape.hpp \ common_structures.hpp \ fribidi.hpp \ + text_layout.hpp \ + intrusive_vector.hpp diff --git a/drape_frontend/intrusive_vector.hpp b/drape_frontend/intrusive_vector.hpp new file mode 100644 index 0000000000..30e5fad580 --- /dev/null +++ b/drape_frontend/intrusive_vector.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "../base/assert.hpp" + +#include "../std/stdint.hpp" + +namespace df +{ + +enum FillDirection +{ + Forward, + Backward +}; + +template +class IntrusiveVector +{ +public: + IntrusiveVector(void * memoryBuffer, uint32_t byteCount) + : m_memory(reinterpret_cast(memoryBuffer)) + , m_direction(Forward) + { + ASSERT(byteCount % sizeof(T) == 0, ()); + m_capacity = byteCount / sizeof(T); + m_size = 0; + } + + void SetFillDirection(FillDirection direction) + { + ASSERT(m_size == 0, ()); + m_direction = direction; + } + + void PushBack(T const & value) + { + ASSERT(m_size < m_capacity, ()); + if (m_direction == Forward) + m_memory[m_size++] = value; + else + { + m_memory[m_capacity - m_size - 1] = value; + m_size++; + } + } + +private: + T * m_memory; + FillDirection m_direction; + uint32_t m_capacity; + uint32_t m_size; +}; + +} diff --git a/drape_frontend/path_text_shape.cpp b/drape_frontend/path_text_shape.cpp index 83ed02bfe3..a3101b2dda 100644 --- a/drape_frontend/path_text_shape.cpp +++ b/drape_frontend/path_text_shape.cpp @@ -1,4 +1,7 @@ #include "path_text_shape.hpp" +#include "text_layout.hpp" +#include "visual_params.hpp" +#include "intrusive_vector.hpp" #include "../drape/shader_def.hpp" #include "../drape/attribute_provider.hpp" @@ -18,332 +21,257 @@ #include "../std/algorithm.hpp" #include "../std/vector.hpp" -namespace df -{ - -using m2::PointF; using m2::Spline; using glsl_types::vec2; -using glsl_types::vec4; namespace { - static float const realFontSize = 28.0f; - - struct Buffer + struct AccumulateRect { - vector m_pos; - vector m_uvs; - vector m_baseColor; - vector m_outlineColor; - vector m_info; - float m_offset; - float m_maxSize; - - void addSizes(float x, float y) + ScreenBase const & m_screen; + m2::RectD m_pixelRect; + AccumulateRect(ScreenBase const & screen) + : m_screen(screen) { - if (x > m_maxSize) - m_maxSize = x; - if (y > m_maxSize) - m_maxSize = y; } + + void operator()(m2::PointF const & pt) + { + m_pixelRect.Add(m_screen.GtoP(pt)); + } + }; + + class PathTextHandle : public dp::OverlayHandle + { + public: + static const uint8_t PathGlyphPositionID = 1; + + PathTextHandle(m2::SharedSpline const & spl, + df::SharedTextLayout const & layout, + float const mercatorOffset, + float const depth) + : OverlayHandle(FeatureID(), dp::Center, depth) + , m_spline(spl) + , m_layout(layout) + , m_splineOffset(mercatorOffset) + { + } + + void Update(ScreenBase const & screen) + { + m_scalePtoG = screen.GetScale(); + GetBegEnd(m_begin, m_end); + + SetIsValid(!m_begin.BeginAgain() && !m_end.BeginAgain()); + if (!IsValid()) + return; + + if (screen.GtoP(m_end.m_pos).x < screen.GtoP(m_begin.m_pos).x) + m_isForward = false; + else + m_isForward = true; + } + + m2::RectD GetPixelRect(ScreenBase const & screen) const + { + ASSERT(IsValid(), ()); + ASSERT(!m_begin.BeginAgain(), ()); + ASSERT(!m_end.BeginAgain(), ()); + + AccumulateRect f(screen); + m_spline->ForEachNode(m_begin, m_end, f); + float const pixelHeight = m_layout->GetPixelHeight(); + f.m_pixelRect.Inflate(2 * pixelHeight, 2 * pixelHeight); + return f.m_pixelRect; + } + + void GetAttributeMutation(dp::RefPointer mutator) const + { + ASSERT(IsValid(), ()); + uint32_t byteCount = 4 * m_layout->GetGlyphCount() * sizeof(glsl_types::vec2); + void * buffer = mutator->AllocateMutationBuffer(byteCount); + df::IntrusiveVector positions(buffer, byteCount); + // m_splineOffset set offset to Center of text. + // By this we calc offset for start of text in mercator + m_layout->LayoutPathText(m_begin, m_scalePtoG, positions, m_isForward); + + TOffsetNode const & node = GetOffsetNode(PathGlyphPositionID); + dp::MutateNode mutateNode; + mutateNode.m_region = node.second; + mutateNode.m_data = dp::MakeStackRefPointer(buffer); + mutator->AddMutation(node.first, mutateNode); + } + + private: + void GetBegEnd(Spline::iterator & beg, Spline::iterator & end) const + { + beg = m_spline.CreateIterator(); + end = m_spline.CreateIterator(); + float const textLangth = m_layout->GetPixelLength() * m_scalePtoG; + float const step = max(0.0f, m_splineOffset - textLangth / 2.0f); + if (step > 0.0f) + beg.Step(step); + end.Step(step + textLangth); + } + + private: + m2::SharedSpline m_spline; + m2::Spline::iterator m_begin; + m2::Spline::iterator m_end; + + df::SharedTextLayout m_layout; + float m_scalePtoG; + float m_splineOffset; + bool m_isForward; + }; + + void BatchPathText(m2::SharedSpline const & spline, + buffer_vector const & offsets, + float depth, + dp::RefPointer batcher, + df::SharedTextLayout const & layout, + vector & positions, + vector & texCoord, + vector & fontColor, + vector & outlineColor) + { + ASSERT(!offsets.empty(), ()); + layout->InitPathText(depth, texCoord, fontColor, outlineColor); + + dp::GLState state(gpu::PATH_FONT_PROGRAM, dp::GLState::OverlayLayer); + state.SetTextureSet(layout->GetTextureSet()); + state.SetBlending(dp::Blending(true)); + + for (size_t i = 0; i < offsets.size(); ++i) + { + dp::AttributeProvider provider(4, 4 * layout->GetGlyphCount()); + { + dp::BindingInfo positionBind(1, PathTextHandle::PathGlyphPositionID); + dp::BindingDecl & decl = positionBind.GetBindingDecl(0); + decl.m_attributeName = "a_position"; + decl.m_componentCount = 2; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(0, positionBind, dp::MakeStackRefPointer(&positions[0])); + } + { + dp::BindingInfo texCoordBind(1); + dp::BindingDecl & decl = texCoordBind.GetBindingDecl(0); + decl.m_attributeName = "a_texcoord"; + decl.m_componentCount = 4; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(1, texCoordBind, dp::MakeStackRefPointer(&texCoord[0])); + } + { + dp::BindingInfo fontColorBind(1); + dp::BindingDecl & decl = fontColorBind.GetBindingDecl(0); + decl.m_attributeName = "a_color"; + decl.m_componentCount = 4; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(2, fontColorBind, dp::MakeStackRefPointer(&fontColor[0])); + } + { + dp::BindingInfo outlineColorBind(1); + dp::BindingDecl & decl = outlineColorBind.GetBindingDecl(0); + decl.m_attributeName = "a_outline_color"; + decl.m_componentCount = 4; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(3, outlineColorBind, dp::MakeStackRefPointer(&outlineColor[0])); + } + + dp::OverlayHandle * handle = new PathTextHandle(spline, layout, offsets[i], depth); + batcher->InsertListOfStrip(state, dp::MakeStackRefPointer(&provider), dp::MovePointer(handle), 4); + } + } } -PathTextShape::PathTextShape(m2::SharedSpline const & spline, PathTextViewParams const & params) +namespace df +{ + +PathTextShape::PathTextShape(m2::SharedSpline const & spline, + PathTextViewParams const & params, + float const scaleGtoP) : m_spline(spline) , m_params(params) + , m_scaleGtoP(scaleGtoP) { } void PathTextShape::Draw(dp::RefPointer batcher, dp::RefPointer textures) const { - strings::UniString const text = strings::MakeUniString(m_params.m_text); - float const fontSize = m_params.m_textFont.m_size; + SharedTextLayout layout(new TextLayout(strings::MakeUniString(m_params.m_text), + m_params.m_textFont, + textures)); - // Fill buffers - int const cnt = text.size(); - vector buffers(1); - float const needOutline = m_params.m_textFont.m_needOutline; - float length = 0.0f; + //we leave a little space on either side of the text that would + //remove the comparison for equality of spline portions + float const TextBorder = 4.0f; + float const textLength = TextBorder + layout->GetPixelLength(); + float const textHalfLength = textLength / 2.0f; + float const pathGlbLength = m_spline->GetLength(); - int textureSet; - for (int i = 0; i < cnt; i++) + // on next readable scale m_scaleGtoP will be twice + if (textLength > pathGlbLength * 2 * m_scaleGtoP) + return; + + float const pathLength = m_scaleGtoP * m_spline->GetLength(); + + /// copied from old code + /// @todo Choose best constant for minimal space. + float const etalonEmpty = max(200 * df::VisualParams::Instance().GetVisualScale(), (double)textLength); + float const minPeriodSize = etalonEmpty + textLength; + float const twoTextAndEmpty = minPeriodSize + textLength; + + uint32_t glyphCount = layout->GetGlyphCount(); + vector positions(glyphCount, vec2(0.0, 0.0)); + vector texCoords(glyphCount); + vector fontColor(glyphCount); + vector outlineColor(glyphCount); + buffer_vector offsets; + + float const scalePtoG = 1.0f / m_scaleGtoP; + + if (pathLength < twoTextAndEmpty) { - dp::TextureSetHolder::GlyphRegion region; - textures->GetGlyphRegion(text[i], region); - float xOffset, yOffset, advance; - m2::PointU pixelSize; - region.GetMetrics(xOffset, yOffset, advance); - region.GetPixelSize(pixelSize); - float halfWidth = pixelSize.x / 2.0f; - float halfHeight = pixelSize.y / 2.0f; - float const aspect = fontSize / realFontSize; + // if we can't place 2 text and empty part on path + // we place only one text on center of path + offsets.push_back(pathGlbLength / 2.0f); - textureSet = region.GetTextureNode().m_textureSet; - if (buffers.size() < textureSet) - buffers.resize(textureSet + 1); - - Buffer & curBuffer = buffers[textureSet]; - - yOffset *= aspect; - xOffset *= aspect; - halfWidth *= aspect; - halfHeight *= aspect; - advance *= aspect; - length += advance; - - curBuffer.addSizes(halfWidth, halfHeight); - - curBuffer.m_info.push_back( - LetterInfo(xOffset, yOffset, advance + curBuffer.m_offset, halfWidth, halfHeight)); - - for (int i = 0; i < buffers.size(); ++i) - buffers[i].m_offset += advance; - - curBuffer.m_offset = 0; - - - m2::RectF const & rect = region.GetTexRect(); - float const textureNum = (region.GetTextureNode().m_textureOffset << 1) + needOutline; - - dp::ColorF const base(m_params.m_textFont.m_color); - dp::ColorF const outline(m_params.m_textFont.m_outlineColor); - - curBuffer.m_uvs.push_back(vec4(rect.minX(), rect.maxY(), textureNum, m_params.m_depth)); - curBuffer.m_uvs.push_back(vec4(rect.minX(), rect.minY(), textureNum, m_params.m_depth)); - curBuffer.m_uvs.push_back(vec4(rect.maxX(), rect.maxY(), textureNum, m_params.m_depth)); - curBuffer.m_uvs.push_back(vec4(rect.maxX(), rect.minY(), textureNum, m_params.m_depth)); - - int const newSize = curBuffer.m_baseColor.size() + 4; - curBuffer.m_baseColor.resize(newSize, base); - curBuffer.m_outlineColor.resize(newSize, outline); - curBuffer.m_pos.resize(newSize, vec2(0.0f, 0.0f)); } - - for (int i = 0; i < buffers.size(); ++i) + else if (pathLength < twoTextAndEmpty + minPeriodSize) { - if (buffers[i].m_pos.empty()) - continue; + // if we can't place 3 text and 2 empty path + // we place 2 text with empty space beetwen + // and some offset from path end + float const endOffset = (pathLength - (2 * textLength + etalonEmpty)) / 2; - dp::GLState state(gpu::PATH_FONT_PROGRAM, dp::GLState::OverlayLayer); - state.SetTextureSet(i); - state.SetBlending(dp::Blending(true)); - - dp::AttributeProvider provider(4, buffers[i].m_pos.size()); - { - dp::BindingInfo position(1, PathTextHandle::DirectionAttributeID); - dp::BindingDecl & decl = position.GetBindingDecl(0); - decl.m_attributeName = "a_position"; - decl.m_componentCount = 2; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(0, position, dp::MakeStackRefPointer(&buffers[i].m_pos[0])); - } - { - dp::BindingInfo texcoord(1); - dp::BindingDecl & decl = texcoord.GetBindingDecl(0); - decl.m_attributeName = "a_texcoord"; - decl.m_componentCount = 4; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(1, texcoord, dp::MakeStackRefPointer(&buffers[i].m_uvs[0])); - } - { - dp::BindingInfo base_color(1); - dp::BindingDecl & decl = base_color.GetBindingDecl(0); - decl.m_attributeName = "a_color"; - decl.m_componentCount = 4; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(2, base_color, dp::MakeStackRefPointer(&buffers[i].m_baseColor[0])); - } - { - dp::BindingInfo outline_color(1); - dp::BindingDecl & decl = outline_color.GetBindingDecl(0); - decl.m_attributeName = "a_outline_color"; - decl.m_componentCount = 4; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(3, outline_color, dp::MakeStackRefPointer(&buffers[i].m_outlineColor[0])); - } - - dp::OverlayHandle * handle = new PathTextHandle(m_spline, m_params, buffers[i].m_info, buffers[i].m_maxSize, length); - - batcher->InsertListOfStrip(state, dp::MakeStackRefPointer(&provider), dp::MovePointer(handle), 4); + // division on m_scaleGtoP give as global coord frame (Mercator) + offsets.push_back((endOffset + textHalfLength) * scalePtoG); + offsets.push_back((pathLength - (textHalfLength + endOffset)) * scalePtoG); } -} - -m2::RectD PathTextHandle::GetPixelRect(ScreenBase const & screen) const -{ - int const cnt = m_infos.size(); - - vec2 const & v1 = m_positions[1]; - vec2 const & v2 = m_positions[2]; - PointF centr((v1.x + v2.x) / 2.0f, (v1.y + v2.y) / 2.0f); - centr = screen.GtoP(centr); - float minx, maxx, miny, maxy; - minx = maxx = centr.x; - miny = maxy = centr.y; - - for (int i = 1; i < cnt; i++) - { - vec2 const & v1 = m_positions[i * 4 + 1]; - vec2 const & v2 = m_positions[i * 4 + 2]; - PointF centr((v1.x + v2.x) / 2.0f, (v1.y + v2.y) / 2.0f); - centr = screen.GtoP(centr); - if(centr.x > maxx) - maxx = centr.x; - if(centr.x < minx) - minx = centr.x; - if(centr.y > maxy) - maxy = centr.y; - if(centr.y < miny) - miny = centr.y; - } - - return m2::RectD(minx - m_maxSize, miny - m_maxSize, maxx + m_maxSize, maxy + m_maxSize); -} - -PathTextHandle::PathTextHandle(m2::SharedSpline const & spl, PathTextViewParams const & params, - vector const & info, float maxSize, float textLength) - : OverlayHandle(FeatureID() - , dp::Center, 0.0f) - , m_path(spl) - , m_params(params) - , m_infos(info) - , m_scaleFactor(1.0f) - , m_positions(info.size() * 4) - , m_maxSize(maxSize) - , m_textLength(textLength) -{ -} - -void PathTextHandle::Update(ScreenBase const & screen) -{ - switch(ChooseDirection(screen)) - { - case -1: - DrawReverse(screen); - break; - case 0: - ClearPositions(); - break; - case 1: - DrawForward(screen); - break; - } -} - -int PathTextHandle::ChooseDirection(ScreenBase const & screen) -{ - m_scaleFactor = screen.GetScale(); - Spline::iterator itr = m_path.CreateIterator(); - itr.Step(m_params.m_offsetStart * m_scaleFactor); - PointF const p1 = screen.GtoP(itr.m_pos); - itr.Step(m_textLength * m_scaleFactor); - PointF const p2 = screen.GtoP(itr.m_pos); - if (itr.BeginAgain()) - return 0; - - if ((p2 - p1).x >= 0 ) - return 1; else - return -1; -} - -void PathTextHandle::ClearPositions() -{ - std::fill(m_positions.begin(), m_positions.end(), vec2(0.0f, 0.0f)); -} - -void PathTextHandle::DrawReverse(ScreenBase const & screen) -{ - m_scaleFactor = screen.GetScale(); - int const cnt = m_infos.size(); - Spline::iterator itr = m_path.CreateIterator(); - itr.Step(m_params.m_offsetStart * m_scaleFactor); - - for (int i = cnt - 1; i >= 0; i--) { - float const advance = m_infos[i].m_advance * m_scaleFactor; - float const halfWidth = m_infos[i].m_halfWidth; - float const halfHeight = m_infos[i].m_halfHeight; - float const xOffset = m_infos[i].m_xOffset + halfWidth; - float const yOffset = m_infos[i].m_yOffset + halfHeight; - - ASSERT_NOT_EQUAL(advance, 0.0, ()); - PointF const pos = itr.m_pos; - itr.Step(advance); - ASSERT(!itr.BeginAgain(), ()); - - PointF dir = itr.m_avrDir.Normalize(); - PointF norm(-dir.y, dir.x); - PointF norm2 = norm; - dir *= halfWidth * m_scaleFactor; - norm *= halfHeight * m_scaleFactor; - - float const fontSize = m_params.m_textFont.m_size * m_scaleFactor / 2.0f; - PointF const pivot = dir * xOffset / halfWidth + norm * yOffset / halfHeight + pos + norm2 * fontSize; - - int index = i * 4; - m_positions[index++] = pivot + dir + norm; - m_positions[index++] = pivot + dir - norm; - m_positions[index++] = pivot - dir + norm; - m_positions[index++] = pivot - dir - norm; + // here we place 2 text on the ends of path + // then we place as much as possible text on center path uniformly + offsets.push_back(textHalfLength * scalePtoG); + offsets.push_back((pathLength - textHalfLength) * scalePtoG); + float const emptySpace = pathLength - 2 * textLength; + uint32_t textCount = static_cast(ceil(emptySpace / minPeriodSize)); + float const offset = (emptySpace - textCount * textLength) / (textCount + 1); + for (size_t i = 0; i < textCount; ++i) + offsets.push_back((textHalfLength + (textLength + offset) * (i + 1)) * scalePtoG); } -} -void PathTextHandle::DrawForward(ScreenBase const & screen) -{ - m_scaleFactor = screen.GetScale(); - int const cnt = m_infos.size(); - Spline::iterator itr = m_path.CreateIterator(); - itr.Step(m_params.m_offsetStart * m_scaleFactor); - - for (int i = 0; i < cnt; i++) - { - float const advance = m_infos[i].m_advance * m_scaleFactor; - float const halfWidth = m_infos[i].m_halfWidth; - float const halfHeight = m_infos[i].m_halfHeight; - /// TODO Can be optimized later (filling stage) - float const xOffset = m_infos[i].m_xOffset + halfWidth; - float const yOffset = -m_infos[i].m_yOffset - halfHeight; - - ASSERT_NOT_EQUAL(advance, 0.0, ()); - PointF const pos = itr.m_pos; - itr.Step(advance); - ASSERT(!itr.BeginAgain(), ()); - - PointF dir = itr.m_avrDir.Normalize(); - PointF norm(-dir.y, dir.x); - PointF norm2 = norm; - dir *= halfWidth * m_scaleFactor; - norm *= halfHeight * m_scaleFactor; - - float const fontSize = m_params.m_textFont.m_size * m_scaleFactor / 2.0f; - PointF const pivot = dir * xOffset / halfWidth + norm * yOffset / halfHeight + pos - norm2 * fontSize; - - int index = i * 4; - m_positions[index++] = pivot - dir - norm; - m_positions[index++] = pivot - dir + norm; - m_positions[index++] = pivot + dir - norm; - m_positions[index++] = pivot + dir + norm; - } -} - - -void PathTextHandle::GetAttributeMutation(dp::RefPointer mutator) const -{ - TOffsetNode const & node = GetOffsetNode(DirectionAttributeID); - dp::MutateNode mutateNode; - mutateNode.m_region = node.second; - mutateNode.m_data = dp::MakeStackRefPointer(&m_positions[0]); - mutator->AddMutation(node.first, mutateNode); + BatchPathText(m_spline, offsets, m_params.m_depth, batcher, layout, + positions, texCoords, fontColor, outlineColor); } } diff --git a/drape_frontend/path_text_shape.hpp b/drape_frontend/path_text_shape.hpp index 5b40ef638e..3921ed6132 100644 --- a/drape_frontend/path_text_shape.hpp +++ b/drape_frontend/path_text_shape.hpp @@ -14,57 +14,18 @@ namespace df { -using m2::PointF; - -struct LetterInfo -{ - LetterInfo(float xOff, float yOff, float adv, float hw, float hh) - : m_xOffset(xOff), m_yOffset(yOff), m_advance(adv), - m_halfWidth(hw), m_halfHeight(hh){} - - LetterInfo(){} - - float m_xOffset; - float m_yOffset; - float m_advance; - float m_halfWidth; - float m_halfHeight; -}; - class PathTextShape : public MapShape { public: - PathTextShape(m2::SharedSpline const & spline, PathTextViewParams const & params); + PathTextShape(m2::SharedSpline const & spline, + PathTextViewParams const & params, + float const scaleGtoP); virtual void Draw(dp::RefPointer batcher, dp::RefPointer textures) const; private: m2::SharedSpline m_spline; PathTextViewParams m_params; -}; - -class PathTextHandle : public dp::OverlayHandle -{ -public: - static const uint8_t DirectionAttributeID = 1; - PathTextHandle(m2::SharedSpline const & spl, PathTextViewParams const & params, - vector const & info, float maxSize, float textLength); - - virtual void Update(ScreenBase const & screen); - void DrawReverse(ScreenBase const & screen); - void DrawForward(ScreenBase const & screen); - void ClearPositions(); - int ChooseDirection(ScreenBase const & screen); - virtual m2::RectD GetPixelRect(ScreenBase const & screen) const; - virtual void GetAttributeMutation(dp::RefPointer mutator) const; - -private: - m2::SharedSpline m_path; - PathTextViewParams m_params; - vector m_infos; - float m_scaleFactor; - mutable vector m_positions; - float m_maxSize; - float m_textLength; + float const m_scaleGtoP; }; } // namespace df diff --git a/drape_frontend/text_layout.cpp b/drape_frontend/text_layout.cpp new file mode 100644 index 0000000000..62d9f98287 --- /dev/null +++ b/drape_frontend/text_layout.cpp @@ -0,0 +1,315 @@ +#include "text_layout.hpp" + +#include "../std/numeric.hpp" +#include "../std/algorithm.hpp" +#include "../std/bind.hpp" + +using glsl_types::vec4; +using glsl_types::Quad4; + +namespace +{ + void FillColor(vector & data, dp::Color const & color) + { + Quad4 c; + c.v[0] = c.v[1] = c.v[2] = c.v[3] = vec4(dp::ColorF(color)); + fill(data.begin(), data.end(), c); + } +} + +namespace df +{ + +class StraightTextHandle : public dp::OverlayHandle +{ +public: + StraightTextHandle(FeatureID const & id, m2::PointD const & pivot, + m2::PointD const & pxSize, m2::PointD const & offset, + double priority) + : OverlayHandle(id, dp::LeftBottom, priority) + , m_pivot(pivot) + , m_offset(offset) + , m_size(pxSize) + { + } + + m2::RectD GetPixelRect(ScreenBase const & screen) const + { + m2::PointD const pivot = screen.GtoP(m_pivot) + m_offset; + return m2::RectD(pivot, pivot + m_size); + } + +private: + m2::PointD m_pivot; + m2::PointD m_offset; + m2::PointD m_size; +}; + +namespace +{ + +#ifdef DEBUG + void ValidateTextureSet(buffer_vector const & metrics) + { + if (metrics.size() < 2) + return; + + ASSERT(metrics[0].IsValid(), ()); + uint32_t textureSet = metrics[0].GetTextureNode().m_textureSet; + for (size_t i = 1; i < metrics.size(); ++i) + { + ASSERT(metrics[i].IsValid(), ()); + ASSERT_EQUAL(metrics[i].GetTextureNode().m_textureSet, textureSet, ()); + } + } +#endif + + float const BASE_HEIGHT = 28.0f; +} + +TextLayout::TextLayout(strings::UniString const & string, + df::FontDecl const & font, + dp::RefPointer textures) + : m_font(font) + , m_textSizeRatio(font.m_size / BASE_HEIGHT) +{ + ASSERT(!string.empty(), ()); + m_metrics.reserve(string.size()); + for_each(string.begin(), string.end(), bind(&TextLayout::InitMetric, this, _1, textures)); +#ifdef DEBUG + ValidateTextureSet(m_metrics); +#endif +} + +dp::OverlayHandle * TextLayout::LayoutText(const FeatureID & featureID, + m2::PointF const & pivot, + m2::PointF const & pixelOffset, + float depth, + vector & positions, + vector & texCoord, + vector & fontColor, + vector & outlineColor) const +{ + STATIC_ASSERT(sizeof(vec4) == 4 * sizeof(float)); + STATIC_ASSERT(sizeof(Quad4) == 4 * sizeof(vec4)); + + size_t glyphCount = GetGlyphCount(); + ASSERT(glyphCount <= positions.size(), ()); + ASSERT(glyphCount <= texCoord.size(), ()); + ASSERT(glyphCount <= fontColor.size(), ()); + ASSERT(glyphCount <= outlineColor.size(), ()); + + FillColor(fontColor, m_font.m_color); + FillColor(outlineColor, m_font.m_outlineColor); + + float glyphOffset = 0.0; + for (size_t i = 0; i < glyphCount; ++i) + { + GlyphRegion const & region = m_metrics[i]; + ASSERT(region.IsValid(), ()); + GetTextureQuad(region, depth, texCoord[i]); + + float xOffset, yOffset, advance; + region.GetMetrics(xOffset, yOffset, advance); + + xOffset *= m_textSizeRatio; + yOffset *= m_textSizeRatio; + advance *= m_textSizeRatio; + + m2::PointU size; + region.GetPixelSize(size); + double const h = size.y * m_textSizeRatio; + double const w = size.x * m_textSizeRatio; + + Quad4 & position = positions[i]; + position.v[0] = vec4(pivot, m2::PointF(glyphOffset + xOffset, yOffset) + pixelOffset); + position.v[1] = vec4(pivot, m2::PointF(glyphOffset + xOffset, yOffset + h) + pixelOffset); + position.v[2] = vec4(pivot, m2::PointF(glyphOffset + w + xOffset, yOffset) + pixelOffset); + position.v[3] = vec4(pivot, m2::PointF(glyphOffset + w + xOffset, yOffset + h) + pixelOffset); + glyphOffset += advance; + } + + return new StraightTextHandle(featureID, pivot, m2::PointD(glyphOffset, m_font.m_size), + pixelOffset, depth); +} + +void TextLayout::InitPathText(float depth, + vector & texCoord, + vector & fontColor, + vector & outlineColor) const +{ + STATIC_ASSERT(sizeof(vec4) == 4 * sizeof(float)); + STATIC_ASSERT(sizeof(Quad4) == 4 * sizeof(vec4)); + + size_t glyphCount = GetGlyphCount(); + ASSERT(glyphCount <= texCoord.size(), ()); + ASSERT(glyphCount <= fontColor.size(), ()); + ASSERT(glyphCount <= outlineColor.size(), ()); + + FillColor(fontColor, m_font.m_color); + FillColor(outlineColor, m_font.m_outlineColor); + + for (size_t i = 0; i < glyphCount; ++i) + GetTextureQuad(m_metrics[i], depth, texCoord[i]); +} + +void TextLayout::LayoutPathText(m2::Spline::iterator const & iterator, + float const scalePtoG, + IntrusiveVector & positions, + bool isForwardDirection) const +{ + if (!isForwardDirection) + positions.SetFillDirection(df::Backward); + + m2::Spline::iterator itr = iterator; + + uint32_t glyphCount = GetGlyphCount(); + int32_t startIndex = isForwardDirection ? 0 : glyphCount - 1; + int32_t endIndex = isForwardDirection ? glyphCount : -1; + int32_t incSign = isForwardDirection ? 1 : -1; + + for (int32_t i = startIndex; i != endIndex; i += incSign) + { + float xOffset, yOffset, advance; + float halfWidth, halfHeight; + GetMetrics(i, xOffset, yOffset, advance, halfWidth, halfHeight); + advance *= scalePtoG; + + ASSERT_NOT_EQUAL(advance, 0.0, ()); + m2::PointF const pos = itr.m_pos; + itr.Step(advance); + ASSERT(!itr.BeginAgain(), ()); + + m2::PointF dir = itr.m_avrDir.Normalize(); + m2::PointF norm(-dir.y, dir.x); + m2::PointF norm2 = norm; + dir *= halfWidth * scalePtoG; + norm *= halfHeight * scalePtoG; + + float const halfFontSize = m_textSizeRatio * scalePtoG / 2.0f; + m2::PointF const dirComponent = dir * xOffset / halfWidth; + m2::PointF const normalComponent = -norm * incSign * yOffset / halfHeight; + m2::PointF const fontSizeComponent = norm2 * incSign * halfFontSize; + m2::PointF const pivot = dirComponent + normalComponent + pos - fontSizeComponent; + + positions.PushBack(glsl_types::vec2(pivot - dir + norm)); + positions.PushBack(glsl_types::vec2(pivot - dir - norm)); + positions.PushBack(glsl_types::vec2(pivot + dir + norm)); + positions.PushBack(glsl_types::vec2(pivot + dir - norm)); + } +} + +uint32_t TextLayout::GetGlyphCount() const +{ + return m_metrics.size(); +} + +uint32_t TextLayout::GetTextureSet() const +{ + return m_metrics[0].GetTextureNode().m_textureSet; +} + +float TextLayout::GetPixelLength() const +{ + return m_textSizeRatio * accumulate(m_metrics.begin(), m_metrics.end(), 0.0, + bind(&TextLayout::AccumulateAdvance, this, _1, _2)); +} + +float TextLayout::GetPixelHeight() const +{ + return m_font.m_size; +} + +void TextLayout::GetTextureQuad(GlyphRegion const & region, + float depth, + Quad4 & quad) const +{ + ASSERT(region.IsValid(), ()); + + m2::RectF const & rect = region.GetTexRect(); + uint8_t needOutline = m_font.m_needOutline ? 1 : 0; + float textureOffset = static_cast((region.GetTextureNode().m_textureOffset << 1) + needOutline); + quad.v[0] = vec4(rect.minX(), rect.minY(), textureOffset, depth); + quad.v[1] = vec4(rect.minX(), rect.maxY(), textureOffset, depth); + quad.v[2] = vec4(rect.maxX(), rect.minY(), textureOffset, depth); + quad.v[3] = vec4(rect.maxX(), rect.maxY(), textureOffset, depth); +} + +float TextLayout::AccumulateAdvance(double const & currentValue, GlyphRegion const & reg1) const +{ + ASSERT(reg1.IsValid(), ()); + + return currentValue + reg1.GetAdvance(); +} + +void TextLayout::InitMetric(strings::UniChar const & unicodePoint, + dp::RefPointer textures) +{ + GlyphRegion region; + if (textures->GetGlyphRegion(unicodePoint, region)) + m_metrics.push_back(region); +} + +void TextLayout::GetMetrics(int32_t const index, float & xOffset, float & yOffset, float & advance, + float & halfWidth, float & halfHeight) const +{ + GlyphRegion const & region = m_metrics[index]; + m2::PointU size; + region.GetPixelSize(size); + region.GetMetrics(xOffset, yOffset, advance); + + halfWidth = m_textSizeRatio * size.x / 2.0f; + halfHeight = m_textSizeRatio * size.y / 2.0f; + + xOffset = xOffset * m_textSizeRatio + halfWidth; + yOffset = yOffset * m_textSizeRatio + halfHeight; + advance *= m_textSizeRatio; +} + +/////////////////////////////////////////////////////////////// + +SharedTextLayout::SharedTextLayout() +{ +} + +SharedTextLayout::SharedTextLayout(TextLayout * layout) + : m_layout(layout) +{ +} + +SharedTextLayout::SharedTextLayout(SharedTextLayout const & other) +{ + m_layout = other.m_layout; +} + +SharedTextLayout const & SharedTextLayout::operator=(SharedTextLayout const & other) +{ + if (&other != this) + m_layout = other.m_layout; + + return *this; +} + +bool SharedTextLayout::IsNull() const +{ + return m_layout == NULL; +} + +void SharedTextLayout::Reset(TextLayout * layout) +{ + m_layout.reset(layout); +} + +TextLayout * SharedTextLayout::operator->() +{ + return m_layout.get(); +} + +TextLayout const * SharedTextLayout::operator->() const +{ + return m_layout.get(); +} + + + +} diff --git a/drape_frontend/text_layout.hpp b/drape_frontend/text_layout.hpp new file mode 100644 index 0000000000..6cf6293904 --- /dev/null +++ b/drape_frontend/text_layout.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include "common_structures.hpp" +#include "shape_view_params.hpp" +#include "intrusive_vector.hpp" + +#include "../drape/pointers.hpp" +#include "../drape/texture_set_holder.hpp" +#include "../drape/overlay_handle.hpp" + +#include "../geometry/spline.hpp" + +#include "../base/string_utils.hpp" +#include "../base/buffer_vector.hpp" + +#include "../std/vector.hpp" +#include "../std/shared_ptr.hpp" + +namespace df +{ + +class TextLayout +{ + typedef dp::TextureSetHolder::GlyphRegion GlyphRegion; +public: + TextLayout(strings::UniString const & string, + df::FontDecl const & font, + dp::RefPointer textures); + + dp::OverlayHandle * LayoutText(FeatureID const & featureID, + m2::PointF const & pivot, + m2::PointF const & pixelOffset, + float depth, + vector & positions, + vector & texCoord, + vector & fontColor, + vector & outlineColor) const; + + void InitPathText(float depth, + vector & texCoord, + vector & fontColor, + vector & outlineColor) const; + void LayoutPathText(m2::Spline::iterator const & iterator, + float const scalePtoG, + IntrusiveVector & positions, + bool isForwardDirection) const; + + uint32_t GetGlyphCount() const; + uint32_t GetTextureSet() const; + float GetPixelLength() const; + float GetPixelHeight() const; + +private: + void GetTextureQuad(GlyphRegion const & region, float depth, glsl_types::Quad4 & quad) const; + float AccumulateAdvance(double const & currentValue, GlyphRegion const & reg2) const; + void InitMetric(strings::UniChar const & unicodePoint, dp::RefPointer textures); + + void GetMetrics(int32_t const index, float & xOffset, float & yOffset, float & advance, + float & halfWidth, float & halfHeight) const; + +private: + buffer_vector m_metrics; + df::FontDecl m_font; + float m_textSizeRatio; +}; + +class SharedTextLayout +{ +public: + SharedTextLayout(); + SharedTextLayout(TextLayout * layout); + SharedTextLayout(SharedTextLayout const & other); + SharedTextLayout const & operator= (SharedTextLayout const & other); + + bool IsNull() const; + void Reset(TextLayout * layout); + + TextLayout * operator->(); + TextLayout const * operator->() const; + +private: + shared_ptr m_layout; +}; + +} diff --git a/drape_frontend/text_shape.cpp b/drape_frontend/text_shape.cpp index 61eb793998..8c2b9c6914 100644 --- a/drape_frontend/text_shape.cpp +++ b/drape_frontend/text_shape.cpp @@ -1,8 +1,8 @@ #include "text_shape.hpp" #include "common_structures.hpp" +#include "text_layout.hpp" #include "fribidi.hpp" - #include "../drape/shader_def.hpp" #include "../drape/attribute_provider.hpp" #include "../drape/glstate.hpp" @@ -23,14 +23,10 @@ namespace df { using m2::PointF; -using glsl_types::vec2; -using glsl_types::vec3; -using glsl_types::vec4; namespace { - static float const realFontSize = 28.0f; - static float const fontOffset = 1.3f; + float const TEXT_EXPAND_FACTOR = 1.3f; PointF GetShift(dp::Anchor anchor, float width, float height) { @@ -49,25 +45,67 @@ namespace } } - class TextHandle : public dp::OverlayHandle + void BatchText(dp::RefPointer batcher, int32_t textureSet, + vector const & positions, + vector const & texCoord, + vector const & fontColors, + vector const & outlineColor, + size_t glyphCount, + dp::OverlayHandle * handle) { - public: - TextHandle(FeatureID const & id, m2::PointD const & pivot, m2::PointD const & pxSize, - m2::PointD const & offset, double priority) - : OverlayHandle(id, dp::LeftBottom, priority), m_pivot(pivot) - , m_offset(offset), m_size(pxSize) {} + ASSERT(glyphCount <= positions.size(), ()); + ASSERT(positions.size() == texCoord.size(), ()); + ASSERT(positions.size() == fontColors.size(), ()); + ASSERT(positions.size() == outlineColor.size(), ()); - m2::RectD GetPixelRect(ScreenBase const & screen) const + dp::GLState state(gpu::FONT_PROGRAM, dp::GLState::OverlayLayer); + state.SetTextureSet(textureSet); + state.SetBlending(dp::Blending(true)); + + dp::AttributeProvider provider(4, 4 * glyphCount); { - m2::PointD const pivot = screen.GtoP(m_pivot) + m_offset; - return m2::RectD(pivot, pivot + m_size); + dp::BindingInfo position(1); + dp::BindingDecl & decl = position.GetBindingDecl(0); + decl.m_attributeName = "a_position"; + decl.m_componentCount = 4; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(0, position, dp::MakeStackRefPointer((void*)&positions[0])); + } + { + dp::BindingInfo texcoord(1); + dp::BindingDecl & decl = texcoord.GetBindingDecl(0); + decl.m_attributeName = "a_texcoord"; + decl.m_componentCount = 4; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(1, texcoord, dp::MakeStackRefPointer((void*)&texCoord[0])); + } + { + dp::BindingInfo base_color(1); + dp::BindingDecl & decl = base_color.GetBindingDecl(0); + decl.m_attributeName = "a_color"; + decl.m_componentCount = 4; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(2, base_color, dp::MakeStackRefPointer((void*)&fontColors[0])); + } + { + dp::BindingInfo outline_color(1); + dp::BindingDecl & decl = outline_color.GetBindingDecl(0); + decl.m_attributeName = "a_outline_color"; + decl.m_componentCount = 4; + decl.m_componentType = gl_const::GLFloatType; + decl.m_offset = 0; + decl.m_stride = 0; + provider.InitStream(3, outline_color, dp::MakeStackRefPointer((void*)&outlineColor[0])); } - private: - m2::PointD m_pivot; - m2::PointD m_offset; - m2::PointD m_size; - }; + batcher->InsertListOfStrip(state, dp::MakeStackRefPointer(&provider), MovePointer(handle), 4); + } } TextShape::TextShape(m2::PointF const & basePoint, TextViewParams const & params) @@ -76,209 +114,80 @@ TextShape::TextShape(m2::PointF const & basePoint, TextViewParams const & params { } -void TextShape::DrawTextLine(TextLine const & textLine, dp::RefPointer batcher, dp::RefPointer textures) const -{ - int const size = textLine.m_text.size(); - int const maxTextureSetCount = textures->GetMaxTextureSet(); - buffer_vector sizes(maxTextureSetCount); - - for (int i = 0; i < size; i++) - { - dp::TextureSetHolder::GlyphRegion region; - if (!textures->GetGlyphRegion(textLine.m_text[i], region)) - continue; - ++sizes[region.GetTextureNode().m_textureSet]; - } - - for (int i = 0; i < maxTextureSetCount; ++i) - { - if (sizes[i]) - DrawUnicalTextLine(textLine, i, sizes[i], batcher, textures); - } -} - -void TextShape::DrawUnicalTextLine(TextLine const & textLine, int setNum, int letterCount, - dp::RefPointer batcher, dp::RefPointer textures) const -{ - strings::UniString text = textLine.m_text; - float fontSize = textLine.m_font.m_size; - float needOutline = textLine.m_font.m_needOutline; - PointF anchorDelta = textLine.m_offset; - - int const numVert = letterCount * 4; - vector vertex(numVert); - vec4 * vertexPt = &vertex[0]; - vector texture(numVert); - vec4 * texturePt = &texture[0]; - - float stride = 0.0f; - int textureSet; - - dp::TextureSetHolder::GlyphRegion region; - for(int i = 0, j = 0 ; i < text.size() ; i++) - { - if (!textures->GetGlyphRegion(text[i], region)) - continue; - float xOffset, yOffset, advance; - region.GetMetrics(xOffset, yOffset, advance); - float const aspect = fontSize / realFontSize; - advance *= aspect; - if (region.GetTextureNode().m_textureSet != setNum) - { - stride += advance; - continue; - } - - textureSet = region.GetTextureNode().m_textureSet; - m2::RectF const rect = region.GetTexRect(); - float const textureNum = (region.GetTextureNode().m_textureOffset << 1) + needOutline; - m2::PointU pixelSize; - region.GetPixelSize(pixelSize); - float const h = pixelSize.y * aspect; - float const w = pixelSize.x * aspect; - yOffset *= aspect; - xOffset *= aspect; - - PointF const leftBottom(stride + xOffset + anchorDelta.x, yOffset + anchorDelta.y); - PointF const rightBottom(stride + w + xOffset + anchorDelta.x, yOffset + anchorDelta.y); - PointF const leftTop(stride + xOffset + anchorDelta.x, yOffset + h + anchorDelta.y); - PointF const rightTop(stride + w + xOffset + anchorDelta.x, yOffset + h + anchorDelta.y); - - *vertexPt = vec4(m_basePoint, leftTop); - vertexPt++; - *vertexPt = vec4(m_basePoint, leftBottom); - vertexPt++; - *vertexPt = vec4(m_basePoint, rightTop); - vertexPt++; - *vertexPt = vec4(m_basePoint, rightBottom); - vertexPt++; - - *texturePt = vec4(rect.minX(), rect.maxY(), textureNum, m_params.m_depth); - texturePt++; - *texturePt = vec4(rect.minX(), rect.minY(), textureNum, m_params.m_depth); - texturePt++; - *texturePt = vec4(rect.maxX(), rect.maxY(), textureNum, m_params.m_depth); - texturePt++; - *texturePt = vec4(rect.maxX(), rect.minY(), textureNum, m_params.m_depth); - texturePt++; - - j++; - stride += advance; - } - - vector color(numVert, vec4(textLine.m_font.m_color)); - vector color2(numVert, vec4(textLine.m_font.m_outlineColor)); - - dp::GLState state(gpu::FONT_PROGRAM, dp::GLState::OverlayLayer); - state.SetTextureSet(textureSet); - state.SetBlending(dp::Blending(true)); - - dp::AttributeProvider provider(4, 4 * letterCount); - { - dp::BindingInfo position(1); - dp::BindingDecl & decl = position.GetBindingDecl(0); - decl.m_attributeName = "a_position"; - decl.m_componentCount = 4; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(0, position, dp::MakeStackRefPointer((void*)&vertex[0])); - } - { - dp::BindingInfo texcoord(1); - dp::BindingDecl & decl = texcoord.GetBindingDecl(0); - decl.m_attributeName = "a_texcoord"; - decl.m_componentCount = 4; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(1, texcoord, dp::MakeStackRefPointer((void*)&texture[0])); - } - { - dp::BindingInfo base_color(1); - dp::BindingDecl & decl = base_color.GetBindingDecl(0); - decl.m_attributeName = "a_color"; - decl.m_componentCount = 4; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(2, base_color, dp::MakeStackRefPointer((void*)&color[0])); - } - { - dp::BindingInfo outline_color(1); - dp::BindingDecl & decl = outline_color.GetBindingDecl(0); - decl.m_attributeName = "a_outline_color"; - decl.m_componentCount = 4; - decl.m_componentType = gl_const::GLFloatType; - decl.m_offset = 0; - decl.m_stride = 0; - provider.InitStream(3, outline_color, dp::MakeStackRefPointer((void*)&color2[0])); - } - - PointF const dim = PointF(textLine.m_length, fontOffset * textLine.m_font.m_size); - dp::OverlayHandle * handle = new TextHandle(m_params.m_featureID, m_basePoint, dim, anchorDelta, m_params.m_depth); - batcher->InsertListOfStrip(state, dp::MakeStackRefPointer(&provider), MovePointer(handle), 4); -} - void TextShape::Draw(dp::RefPointer batcher, dp::RefPointer textures) const { - strings::UniString const text = fribidi::log2vis(strings::MakeUniString(m_params.m_primaryText)); - int const size = text.size(); - float const fontSize = m_params.m_primaryTextFont.m_size; - float textLength = 0.0f; - vector lines(4); - - for (int i = 0 ; i < size ; i++) - { - dp::TextureSetHolder::GlyphRegion region; - if (!textures->GetGlyphRegion(text[i], region)) - { - LOG(LDEBUG, ("Symbol not found ", text[i])); - continue; - } - float xOffset, yOffset, advance; - region.GetMetrics(xOffset, yOffset, advance); - textLength += advance * fontSize / realFontSize; - } - + ASSERT(!m_params.m_primaryText.empty(), ()); if (m_params.m_secondaryText.empty()) { - PointF const anchorDelta = GetShift(m_params.m_anchor, textLength, fontSize) + m_params.m_primaryOffset; - lines[0] = TextLine(anchorDelta, text, textLength, m_params.m_primaryTextFont); - DrawTextLine(lines[0], batcher, textures); - return; + df::FontDecl const & primaryFont = m_params.m_primaryTextFont; + TextLayout const primaryLayout(fribidi::log2vis(strings::MakeUniString(m_params.m_primaryText)), primaryFont, textures); + PointF const pixelOffset = GetShift(m_params.m_anchor, primaryLayout.GetPixelLength(), primaryFont.m_size) + m_params.m_primaryOffset; + + size_t glyphCount = primaryLayout.GetGlyphCount(); + vector positions(glyphCount); + vector texCoord(glyphCount); + vector fontColor(glyphCount); + vector outlineColor(glyphCount); + + dp::OverlayHandle * handle = primaryLayout.LayoutText(m_params.m_featureID, m_basePoint, + pixelOffset, m_params.m_depth, + positions, texCoord, fontColor, outlineColor); + BatchText(batcher, primaryLayout.GetTextureSet(), + positions, texCoord, + fontColor, outlineColor, + glyphCount, handle); } - - strings::UniString const auxText = fribidi::log2vis(strings::MakeUniString(m_params.m_secondaryText)); - int const auxSize = auxText.size(); - float const auxFontSize = m_params.m_secondaryTextFont.m_size; - float auxTextLength = 0.0f; - - for (int i = 0 ; i < auxSize ; i++) + else { - dp::TextureSetHolder::GlyphRegion region; - if (!textures->GetGlyphRegion(auxText[i], region)) + df::FontDecl const & primaryFont = m_params.m_primaryTextFont; + df::FontDecl const & secondaryFont = m_params.m_secondaryTextFont; + + TextLayout const primaryLayout(fribidi::log2vis(strings::MakeUniString(m_params.m_primaryText)), primaryFont, textures); + TextLayout const secondaryLayout(fribidi::log2vis(strings::MakeUniString(m_params.m_secondaryText)), secondaryFont, textures); + + float const primaryTextLength = primaryLayout.GetPixelLength(); + float const secondaryTextLength = secondaryLayout.GetPixelLength(); + bool const isPrimaryLonger = primaryTextLength > secondaryTextLength; + float const maxLength = max(primaryTextLength, secondaryTextLength); + float const minLength = min(primaryTextLength, secondaryTextLength); + float const halfLengthDiff = (maxLength - minLength) / 2.0; + + float const textHeight = TEXT_EXPAND_FACTOR * (primaryFont.m_size + secondaryFont.m_size); + PointF const anchorOffset = GetShift(m_params.m_anchor, maxLength, textHeight); + + float const primaryDx = isPrimaryLonger ? 0.0f : halfLengthDiff; + float const primaryDy = (1.0f - TEXT_EXPAND_FACTOR) * primaryFont.m_size - TEXT_EXPAND_FACTOR * secondaryFont.m_size; + PointF const primaryPixelOffset = PointF(primaryDx, primaryDy) + anchorOffset + m_params.m_primaryOffset; + + size_t glyphCount = max(primaryLayout.GetGlyphCount(), secondaryLayout.GetGlyphCount()); + vector positions(glyphCount); + vector texCoord(glyphCount); + vector fontColor(glyphCount); + vector outlineColor(glyphCount); + { - LOG(LDEBUG, ("Symbol not found(aux) ", auxText[i])); - continue; + dp::OverlayHandle * handle = primaryLayout.LayoutText(m_params.m_featureID, m_basePoint, + primaryPixelOffset, m_params.m_depth, + positions, texCoord, fontColor, outlineColor); + BatchText(batcher, primaryLayout.GetTextureSet(), + positions, texCoord, + fontColor, outlineColor, + primaryLayout.GetGlyphCount(), handle); + } + + float const secondaryDx = isPrimaryLonger ? halfLengthDiff : 0.0f; + PointF const secondaryPixelOffset = PointF(secondaryDx, 0.0f) + anchorOffset + m_params.m_primaryOffset; + + { + dp::OverlayHandle * handle = secondaryLayout.LayoutText(m_params.m_featureID, m_basePoint, + secondaryPixelOffset, m_params.m_depth, + positions, texCoord, fontColor, outlineColor); + BatchText(batcher, secondaryLayout.GetTextureSet(), + positions, texCoord, + fontColor, outlineColor, + secondaryLayout.GetGlyphCount(), handle); } - float xOffset, yOffset, advance; - region.GetMetrics(xOffset, yOffset, advance); - auxTextLength += advance * auxFontSize / realFontSize; } - - float const length = max(textLength, auxTextLength); - PointF const anchorDelta = GetShift(m_params.m_anchor, length, fontOffset * fontSize + fontOffset * auxFontSize); - float dx = textLength > auxTextLength ? 0.0f : (auxTextLength - textLength) / 2.0f; - PointF const textDelta = PointF(dx, -fontOffset * auxFontSize + (1.0f - fontOffset) * fontSize) + anchorDelta + m_params.m_primaryOffset; - dx = textLength > auxTextLength ? (textLength - auxTextLength) / 2.0f : 0.0f; - PointF const auxTextDelta = PointF(dx, 0.0f) + anchorDelta + m_params.m_primaryOffset; - - lines[0] = TextLine(textDelta, text, textLength, m_params.m_primaryTextFont); - lines[1] = TextLine(auxTextDelta, auxText, auxTextLength, m_params.m_secondaryTextFont); - - DrawTextLine(lines[0], batcher, textures); - DrawTextLine(lines[1], batcher, textures); } } //end of df namespace diff --git a/drape_frontend/text_shape.hpp b/drape_frontend/text_shape.hpp index 13f085b566..f5d8804ee3 100644 --- a/drape_frontend/text_shape.hpp +++ b/drape_frontend/text_shape.hpp @@ -10,32 +10,12 @@ namespace df { -namespace -{ - struct TextLine - { - m2::PointF m_offset; - strings::UniString m_text; - float m_length; - FontDecl m_font; - - TextLine() {} - TextLine(m2::PointF const & offset, strings::UniString const & text, float length, FontDecl const & font) - : m_offset(offset), m_text(text), m_length(length), m_font(font) {} - }; -} - class TextShape : public MapShape { public: TextShape(m2::PointF const & basePoint, TextViewParams const & params); - virtual void Draw(dp::RefPointer batcher, dp::RefPointer textures) const; - -private: - void DrawTextLine(TextLine const & textLine, dp::RefPointer batcher, dp::RefPointer textures) const; - void DrawUnicalTextLine(TextLine const & textLine, int setNum, int letterCount, - dp::RefPointer batcher, dp::RefPointer textures) const; + void Draw(dp::RefPointer batcher, dp::RefPointer textures) const; private: m2::PointF m_basePoint;