Fix text markup background incorrect position with negative line spacing
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-controller-background-actor.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // HEADER
19 #include <dali-toolkit/internal/text/text-controller-background-actor.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/rendering/renderer.h>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/devel-api/controls/control-depth-index-ranges.h>
26 #include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
27 #include <dali-toolkit/internal/text/cursor-helper-functions.h>
28 #include <dali-toolkit/internal/text/text-view.h>
29
30 namespace Dali::Toolkit::Text
31 {
32 namespace
33 {
34 struct BackgroundVertex
35 {
36   Vector2 mPosition; ///< Vertex posiiton
37   Vector4 mColor;    ///< Vertex color
38 };
39
40 struct BackgroundMesh
41 {
42   Vector<BackgroundVertex> mVertices; ///< container of vertices
43   Vector<unsigned short>   mIndices;  ///< container of indices
44 };
45 } // unnamed namespace
46
47 Length CalculateBackgroundLineHeight(LineRun lineRun)
48 {
49   Length height = lineRun.ascender + -(lineRun.descender);
50
51   if(lineRun.lineSpacing > 0)
52   {
53     height += lineRun.lineSpacing;
54   }
55
56   return height;
57 }
58
59 Actor CreateControllerBackgroundActor(const View& textView, const VisualModelPtr& textVisualModel, const LogicalModelPtr& textLogicalModel, Shader& textShaderBackground)
60 {
61   // NOTE: Currently we only support background color for left-to-right text.
62
63   Actor actor;
64
65   Length numberOfGlyphs = textView.GetNumberOfGlyphs();
66   if(numberOfGlyphs > 0u)
67   {
68     Vector<GlyphInfo> glyphs;
69     glyphs.Resize(numberOfGlyphs);
70
71     Vector<Vector2> positions;
72     positions.Resize(numberOfGlyphs);
73
74     // Get the line where the glyphs are laid-out.
75     const LineRun* lineRun         = textVisualModel->mLines.Begin();
76     float          alignmentOffset = lineRun->alignmentOffset;
77     numberOfGlyphs                 = textView.GetGlyphs(glyphs.Begin(),
78                                         positions.Begin(),
79                                         alignmentOffset,
80                                         0u,
81                                         numberOfGlyphs);
82
83     glyphs.Resize(numberOfGlyphs);
84     positions.Resize(numberOfGlyphs);
85
86     const GlyphInfo* const glyphsBuffer    = glyphs.Begin();
87     const Vector2* const   positionsBuffer = positions.Begin();
88
89     BackgroundMesh mesh;
90     mesh.mVertices.Reserve(4u * glyphs.Size());
91     mesh.mIndices.Reserve(6u * glyphs.Size());
92
93     const Vector2 textSize = textView.GetLayoutSize();
94
95     const float offsetX = alignmentOffset + textSize.width * 0.5f;
96     const float offsetY = textSize.height * 0.5f;
97
98     const Vector4* const    backgroundColorsBuffer       = textView.GetBackgroundColors();
99     const ColorIndex* const backgroundColorIndicesBuffer = textView.GetBackgroundColorIndices();
100     const Vector4&          defaultBackgroundColor       = textVisualModel->IsBackgroundEnabled() ? textVisualModel->GetBackgroundColor() : Color::TRANSPARENT;
101     const float             characterSpacing             = textVisualModel->GetCharacterSpacing();
102     Vector<CharacterIndex>& glyphToCharacterMap          = textVisualModel->mGlyphsToCharacters;
103     const CharacterIndex*   glyphToCharacterMapBuffer    = glyphToCharacterMap.Begin();
104     float                   calculatedAdvance            = 0.f;
105
106     Vector4   quad;
107     uint32_t  numberOfQuads = 0u;
108     Length    yLineOffset   = 0;
109     Length    prevLineIndex = 0;
110     LineIndex lineIndex;
111     Length    numberOfLines;
112
113     for(uint32_t i = 0, glyphSize = glyphs.Size(); i < glyphSize; ++i)
114     {
115       const GlyphInfo& glyph = *(glyphsBuffer + i);
116
117       // Get the background color of the character.
118       // The color index zero is reserved for the default background color (i.e. Color::TRANSPARENT)
119       const bool       isMarkupBackground       = textView.IsMarkupBackgroundColorSet();
120       const ColorIndex backgroundColorIndex     = isMarkupBackground ? *(backgroundColorIndicesBuffer + i) : 0u;
121       const bool       isDefaultBackgroundColor = (0u == backgroundColorIndex);
122       const Vector4&   backgroundColor          = isDefaultBackgroundColor ? defaultBackgroundColor : *(backgroundColorsBuffer + backgroundColorIndex - 1u);
123
124       textVisualModel->GetNumberOfLines(i, 1, lineIndex, numberOfLines);
125       Length lineHeight = CalculateBackgroundLineHeight(lineRun[lineIndex]);
126
127       if(lineIndex != prevLineIndex)
128       {
129         yLineOffset += CalculateBackgroundLineHeight(lineRun[prevLineIndex]);
130
131         if(lineRun[prevLineIndex].lineSpacing < 0)
132         {
133           yLineOffset += lineRun[prevLineIndex].lineSpacing;
134         }
135       }
136
137       // Only create quads for glyphs with a background color
138       if(backgroundColor != Color::TRANSPARENT)
139       {
140         const Vector2 position = *(positionsBuffer + i);
141         calculatedAdvance      = GetCalculatedAdvance(*(textLogicalModel->mText.Begin() + (*(glyphToCharacterMapBuffer + i))), characterSpacing, glyph.advance);
142
143         if(i == 0u && glyphSize == 1u) // Only one glyph in the whole text
144         {
145           quad.x = position.x;
146           quad.y = yLineOffset;
147           quad.z = quad.x + std::max(calculatedAdvance, glyph.xBearing + glyph.width);
148           quad.w = lineHeight;
149         }
150         else if((lineIndex != prevLineIndex) || (i == 0u)) // The first glyph in the line
151         {
152           quad.x = position.x;
153           quad.y = yLineOffset;
154           quad.z = quad.x - glyph.xBearing + calculatedAdvance;
155           quad.w = quad.y + lineHeight;
156         }
157         else if(i == glyphSize - 1u) // The last glyph in the whole text
158         {
159           quad.x = position.x - glyph.xBearing;
160           quad.y = yLineOffset;
161           quad.z = quad.x + std::max(calculatedAdvance, glyph.xBearing + glyph.width);
162           quad.w = quad.y + lineHeight;
163         }
164         else // The glyph in the middle of the text
165         {
166           quad.x = position.x - glyph.xBearing;
167           quad.y = yLineOffset;
168           quad.z = quad.x + calculatedAdvance;
169           quad.w = quad.y + lineHeight;
170         }
171
172         BackgroundVertex vertex;
173
174         // Top left
175         vertex.mPosition.x = quad.x - offsetX;
176         vertex.mPosition.y = quad.y - offsetY;
177         vertex.mColor      = backgroundColor;
178         mesh.mVertices.PushBack(vertex);
179
180         // Top right
181         vertex.mPosition.x = quad.z - offsetX;
182         vertex.mPosition.y = quad.y - offsetY;
183         vertex.mColor      = backgroundColor;
184         mesh.mVertices.PushBack(vertex);
185
186         // Bottom left
187         vertex.mPosition.x = quad.x - offsetX;
188         vertex.mPosition.y = quad.w - offsetY;
189         vertex.mColor      = backgroundColor;
190         mesh.mVertices.PushBack(vertex);
191
192         // Bottom right
193         vertex.mPosition.x = quad.z - offsetX;
194         vertex.mPosition.y = quad.w - offsetY;
195         vertex.mColor      = backgroundColor;
196         mesh.mVertices.PushBack(vertex);
197
198         // Six indices in counter clockwise winding
199         mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
200         mesh.mIndices.PushBack(0u + 4 * numberOfQuads);
201         mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
202         mesh.mIndices.PushBack(2u + 4 * numberOfQuads);
203         mesh.mIndices.PushBack(3u + 4 * numberOfQuads);
204         mesh.mIndices.PushBack(1u + 4 * numberOfQuads);
205
206         numberOfQuads++;
207       }
208
209       if(lineIndex != prevLineIndex)
210       {
211         prevLineIndex = lineIndex;
212       }
213     }
214
215     // Only create the background actor if there are glyphs with background color
216     if(mesh.mVertices.Count() > 0u)
217     {
218       Property::Map quadVertexFormat;
219       quadVertexFormat["aPosition"] = Property::VECTOR2;
220       quadVertexFormat["aColor"]    = Property::VECTOR4;
221
222       VertexBuffer quadVertices = VertexBuffer::New(quadVertexFormat);
223       quadVertices.SetData(&mesh.mVertices[0], mesh.mVertices.Size());
224
225       Geometry quadGeometry = Geometry::New();
226       quadGeometry.AddVertexBuffer(quadVertices);
227       quadGeometry.SetIndexBuffer(&mesh.mIndices[0], mesh.mIndices.Size());
228
229       if(!textShaderBackground)
230       {
231         textShaderBackground = Shader::New(SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_VERT, SHADER_TEXT_CONTROLLER_BACKGROUND_SHADER_FRAG);
232       }
233
234       Dali::Renderer renderer = Dali::Renderer::New(quadGeometry, textShaderBackground);
235       renderer.SetProperty(Dali::Renderer::Property::BLEND_MODE, BlendMode::ON);
236       renderer.SetProperty(Dali::Renderer::Property::DEPTH_INDEX, DepthIndex::CONTENT);
237
238       actor = Actor::New();
239       actor.SetProperty(Dali::Actor::Property::NAME, "TextBackgroundColorActor");
240       actor.SetProperty(Actor::Property::PARENT_ORIGIN, ParentOrigin::TOP_LEFT);
241       actor.SetProperty(Actor::Property::ANCHOR_POINT, AnchorPoint::TOP_LEFT);
242       actor.SetProperty(Actor::Property::SIZE, textSize);
243       actor.SetProperty(Actor::Property::COLOR_MODE, USE_OWN_MULTIPLY_PARENT_COLOR);
244       actor.AddRenderer(renderer);
245     }
246   }
247
248   return actor;
249 }
250
251 } // namespace Dali::Toolkit::Text