Remove useless iteration when debug mode
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / shaper.cpp
1 /*
2  * Copyright (c) 2021 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 // CLASS HEADER
19 #include <dali-toolkit/internal/text/shaper.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/devel-api/text-abstraction/shaping.h>
23
24 namespace Dali
25 {
26 namespace Toolkit
27 {
28 namespace Text
29 {
30 CharacterIndex min(CharacterIndex index0,
31                    CharacterIndex index1)
32 {
33   return (index0 < index1) ? index0 : index1;
34 }
35
36 void ShapeText(const Vector<Character>&     text,
37                const Vector<LineBreakInfo>& lineBreakInfo,
38                const Vector<ScriptRun>&     scripts,
39                const Vector<FontRun>&       fonts,
40                CharacterIndex               startCharacterIndex,
41                GlyphIndex                   startGlyphIndex,
42                Length                       numberOfCharacters,
43                Vector<GlyphInfo>&           glyphs,
44                Vector<CharacterIndex>&      glyphToCharacterMap,
45                Vector<Length>&              charactersPerGlyph,
46                Vector<GlyphIndex>&          newParagraphGlyphs)
47 {
48   if(0u == numberOfCharacters)
49   {
50     // Nothing to do if there are no characters.
51     return;
52   }
53
54 #ifdef DEBUG_ENABLED
55   const Length numberOfFontRuns        = fonts.Count();
56   const Length numberOfScriptRuns      = scripts.Count();
57   const Length totalNumberOfCharacters = text.Count();
58 #endif
59
60   DALI_ASSERT_DEBUG((0u != numberOfFontRuns) &&
61                     (totalNumberOfCharacters == fonts[numberOfFontRuns - 1u].characterRun.characterIndex + fonts[numberOfFontRuns - 1u].characterRun.numberOfCharacters) &&
62                     "Toolkit::Text::ShapeText. All characters must have a font set.");
63
64   DALI_ASSERT_DEBUG((0u != numberOfScriptRuns) &&
65                     (totalNumberOfCharacters == scripts[numberOfScriptRuns - 1u].characterRun.characterIndex + scripts[numberOfScriptRuns - 1u].characterRun.numberOfCharacters) &&
66                     "Toolkit::Text::ShapeText. All characters must have a script set.");
67
68   // The text needs to be split in chunks of consecutive characters.
69   // Each chunk must contain characters with the same font id and script set.
70   // A chunk of consecutive characters must not contain a LINE_MUST_BREAK, if there is one a new chunk has to be created.
71
72   TextAbstraction::Shaping shaping = TextAbstraction::Shaping::Get();
73
74   // To shape the text a font and an script is needed.
75
76   // Get the font run containing the startCharacterIndex character.
77   Vector<FontRun>::ConstIterator fontRunIt = fonts.Begin();
78   for(Vector<FontRun>::ConstIterator endIt = fonts.End(); fontRunIt < endIt; ++fontRunIt)
79   {
80     const FontRun& run = *fontRunIt;
81     if(startCharacterIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters)
82     {
83       // Found.
84       break;
85     }
86   }
87
88   // Get the script run containing the startCharacterIndex character.
89   Vector<ScriptRun>::ConstIterator scriptRunIt = scripts.Begin();
90   for(Vector<ScriptRun>::ConstIterator endIt = scripts.End(); scriptRunIt < endIt; ++scriptRunIt)
91   {
92     const ScriptRun& run = *scriptRunIt;
93     if(startCharacterIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters)
94     {
95       // Found.
96       break;
97     }
98   }
99
100   // Index to the the next one to be shaped. Is pointing the character after the last one it was shaped.
101   CharacterIndex previousIndex = 0u;
102
103   // The current font id and script used to shape the text.
104   FontId currentFontId = 0u;
105   Script currentScript = TextAbstraction::UNKNOWN;
106
107   // Reserve some space to allocate the glyphs and the glyph to character map.
108   // There is no way to know the number of glyphs before shaping the text.
109   // To avoid reallocations it's reserved space for a slightly biger number of glyphs than the number of characters.
110
111   GlyphInfo glyphInfo;
112   glyphInfo.isItalicRequired = false;
113   glyphInfo.isBoldRequired   = false;
114
115   const Length currentNumberOfGlyphs  = glyphs.Count();
116   const Length numberOfGlyphsReserved = static_cast<Length>(numberOfCharacters * 1.3f);
117   glyphs.Reserve(currentNumberOfGlyphs + numberOfGlyphsReserved);
118   glyphToCharacterMap.Reserve(currentNumberOfGlyphs + numberOfGlyphsReserved);
119
120   // The actual number of glyphs.
121   Length totalNumberOfGlyphs = currentNumberOfGlyphs;
122   // The number of new glyphs.
123   Length numberOfNewGlyphs = 0u;
124
125   const Character* const     textBuffer                = text.Begin();
126   const LineBreakInfo* const lineBreakInfoBuffer       = lineBreakInfo.Begin();
127   CharacterIndex*            glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
128
129   Length glyphIndex = startGlyphIndex;
130
131   // Traverse the characters and shape the text.
132   const CharacterIndex lastCharacter = startCharacterIndex + numberOfCharacters;
133   for(previousIndex = startCharacterIndex; previousIndex < lastCharacter;)
134   {
135     // Get the font id and the script.
136     const FontRun&   fontRun   = *fontRunIt;
137     const ScriptRun& scriptRun = *scriptRunIt;
138
139     currentFontId               = fontRun.fontId;
140     currentScript               = scriptRun.script;
141     const bool isItalicRequired = fontRun.isItalicRequired;
142     const bool isBoldRequired   = fontRun.isBoldRequired;
143
144     // Get the min index to the last character of both runs.
145     CharacterIndex currentIndex = min(fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters,
146                                       scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters);
147
148     // Check if there is a line must break.
149     bool mustBreak = false;
150
151     // Check if the current index is a new paragraph character.
152     // A new paragraph character is going to be shaped in order to not to mess the conversion tables.
153     // However, the metrics need to be changed in order to not to draw a square.
154     bool isNewParagraph = false;
155
156     for(CharacterIndex index = previousIndex; index < currentIndex; ++index)
157     {
158       mustBreak = TextAbstraction::LINE_MUST_BREAK == *(lineBreakInfoBuffer + index);
159       if(mustBreak)
160       {
161         isNewParagraph = TextAbstraction::IsNewParagraph(*(textBuffer + index));
162         currentIndex   = index + 1u;
163         break;
164       }
165     }
166
167     // Shape the text for the current chunk.
168     const Length numberOfGlyphs = shaping.Shape(textBuffer + previousIndex,
169                                                 (currentIndex - previousIndex), // The number of characters to shape.
170                                                 currentFontId,
171                                                 currentScript);
172
173     // Retrieve the glyphs and the glyph to character conversion map.
174     Vector<GlyphInfo>      tmpGlyphs;
175     Vector<CharacterIndex> tmpGlyphToCharacterMap;
176
177     GlyphInfo glyphInfo;
178     glyphInfo.isItalicRequired = isItalicRequired;
179     glyphInfo.isBoldRequired   = isBoldRequired;
180
181     tmpGlyphs.Resize(numberOfGlyphs, glyphInfo);
182     tmpGlyphToCharacterMap.Resize(numberOfGlyphs);
183     shaping.GetGlyphs(tmpGlyphs.Begin(),
184                       tmpGlyphToCharacterMap.Begin());
185
186     // Update the new indices of the glyph to character map.
187     if(0u != totalNumberOfGlyphs)
188     {
189       for(Vector<CharacterIndex>::Iterator it    = tmpGlyphToCharacterMap.Begin(),
190                                            endIt = tmpGlyphToCharacterMap.End();
191           it != endIt;
192           ++it)
193       {
194         *it += previousIndex;
195       }
196     }
197
198     totalNumberOfGlyphs += numberOfGlyphs;
199     numberOfNewGlyphs += numberOfGlyphs;
200
201     glyphs.Insert(glyphs.Begin() + glyphIndex, tmpGlyphs.Begin(), tmpGlyphs.End());
202     glyphToCharacterMap.Insert(glyphToCharacterMap.Begin() + glyphIndex, tmpGlyphToCharacterMap.Begin(), tmpGlyphToCharacterMap.End());
203     glyphIndex += numberOfGlyphs;
204
205     // Set the buffer pointers again.
206     glyphToCharacterMapBuffer = glyphToCharacterMap.Begin();
207
208     if(isNewParagraph)
209     {
210       // Add the index of the new paragraph glyph to a vector.
211       // Their metrics will be updated in a following step.
212       newParagraphGlyphs.PushBack(glyphIndex - 1u);
213     }
214
215     // Update the iterators to get the next font or script run.
216     if(currentIndex == fontRun.characterRun.characterIndex + fontRun.characterRun.numberOfCharacters)
217     {
218       ++fontRunIt;
219     }
220     if(currentIndex == scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters)
221     {
222       ++scriptRunIt;
223     }
224
225     // Update the previous index.
226     previousIndex = currentIndex;
227   }
228
229   // Update indices.
230   for(Length index = startGlyphIndex + numberOfNewGlyphs; index < totalNumberOfGlyphs; ++index)
231   {
232     CharacterIndex& characterIndex = *(glyphToCharacterMapBuffer + index);
233     characterIndex += numberOfCharacters;
234   }
235
236   // Add the number of characters per glyph.
237   charactersPerGlyph.Reserve(totalNumberOfGlyphs);
238   Length* charactersPerGlyphBuffer = charactersPerGlyph.Begin();
239
240   const GlyphIndex lastGlyph = startGlyphIndex + numberOfNewGlyphs;
241   previousIndex              = startCharacterIndex;
242   for(Length index = startGlyphIndex + 1u; index < lastGlyph; ++index)
243   {
244     const CharacterIndex characterIndex = *(glyphToCharacterMapBuffer + index);
245
246     charactersPerGlyph.Insert(charactersPerGlyphBuffer + index - 1u, characterIndex - previousIndex);
247
248     previousIndex = characterIndex;
249   }
250   charactersPerGlyph.Insert(charactersPerGlyphBuffer + lastGlyph - 1u, numberOfCharacters + startCharacterIndex - previousIndex);
251
252   // Resize the vectors to set the right number of items.
253   glyphs.Resize(totalNumberOfGlyphs);
254   glyphToCharacterMap.Resize(totalNumberOfGlyphs);
255 }
256
257 } // namespace Text
258
259 } // namespace Toolkit
260
261 } // namespace Dali