TextModel - Create paragraph info for a given range of characters inside a text.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / visual-model-impl.cpp
1 /*
2  * Copyright (c) 2015 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/visual-model-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <memory.h>
23
24 namespace Dali
25 {
26
27 namespace Toolkit
28 {
29
30 namespace Text
31 {
32
33 VisualModelPtr VisualModel::New()
34 {
35   return VisualModelPtr( new VisualModel() );
36 }
37
38 void VisualModel::CreateCharacterToGlyphTable( CharacterIndex startIndex,
39                                                Length numberOfCharacters )
40 {
41   if( 0u == numberOfCharacters )
42   {
43     // Nothing to do.
44     return;
45   }
46
47   DALI_ASSERT_DEBUG( mGlyphsPerCharacter.Count() != 0u );
48
49   // Get the total number of characters.
50   const Length totalNumberOfCharacters = ( 0u == mGlyphsToCharacters.Count() ) ? 0u : *( mGlyphsToCharacters.End() - 1u ) + *( mCharactersPerGlyph.End() - 1u ); // Index to the first character + the number of characters that form the last glyph.
51
52   // Whether the current buffer is being updated or is set from scratch.
53   const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
54
55   Vector<GlyphIndex> newCharactersToGlyph;
56   GlyphIndex* charactersToGlyphBuffer = NULL;
57
58   // 1) Reserve some space for the glyph indices to avoid reallocations.
59   if( updateCurrentBuffer )
60   {
61     newCharactersToGlyph.Resize( numberOfCharacters );
62     charactersToGlyphBuffer = newCharactersToGlyph.Begin();
63   }
64   else
65   {
66     mCharactersToGlyph.Resize( numberOfCharacters );
67     charactersToGlyphBuffer = mCharactersToGlyph.Begin() + startIndex;
68   }
69
70   const Length* const glyphsPerCharacterBuffer = mGlyphsPerCharacter.Begin();
71
72   // 2) Traverse the glyphs and set the glyph indices per character.
73
74   // Index to the glyph.
75   const GlyphIndex startGlyphIndex = updateCurrentBuffer ? *( mCharactersToGlyph.Begin() + startIndex ) : 0u;
76   GlyphIndex glyphIndex = startGlyphIndex;
77   CharacterIndex characterIndex = startIndex;
78   const CharacterIndex lastCharacterIndexPlusOne = startIndex + numberOfCharacters;
79   for( Vector<Length>::ConstIterator it = mCharactersPerGlyph.Begin() + glyphIndex,
80          endIt = mCharactersPerGlyph.End();
81        ( it != endIt ) && ( characterIndex < lastCharacterIndexPlusOne );
82        ++it )
83   {
84     const Length numberOfCharactersPerGlyph = *it;
85
86     Length numberOfGlyphs = 0u;
87     // Set the glyph indices.
88     for( Length index = 0u; index < numberOfCharactersPerGlyph; ++index, ++characterIndex )
89     {
90       *charactersToGlyphBuffer = glyphIndex;
91       numberOfGlyphs += *( glyphsPerCharacterBuffer + characterIndex );
92       ++charactersToGlyphBuffer;
93     }
94     glyphIndex += numberOfGlyphs;
95   }
96
97   // If the character to glyph buffer is updated, it needs to be inserted in the model.
98   if( updateCurrentBuffer )
99   {
100     // Update the indices.
101     const Length numberOfGlyphs = glyphIndex - startGlyphIndex;
102     for( Vector<Length>::Iterator it = mCharactersToGlyph.Begin() + startIndex,
103            endIt = mCharactersToGlyph.End();
104          it != endIt;
105          ++it )
106     {
107       *it += numberOfGlyphs;
108     }
109
110     mCharactersToGlyph.Insert( mCharactersToGlyph.Begin() + startIndex,
111                                newCharactersToGlyph.Begin(),
112                                newCharactersToGlyph.End() );
113
114   }
115 }
116
117 void VisualModel::CreateGlyphsPerCharacterTable( CharacterIndex startIndex,
118                                                  Length numberOfCharacters )
119 {
120   if( 0u == numberOfCharacters )
121   {
122     // Nothing to do.
123     return;
124   }
125
126   // Get the total number of characters.
127   const Length totalNumberOfCharacters = ( 0u == mGlyphsToCharacters.Count() ) ? 0u : *( mGlyphsToCharacters.End() - 1u ) + *( mCharactersPerGlyph.End() - 1u ); // Index to the first character + the number of characters that form the last glyph.
128
129   // Whether the current buffer is being updated or is set from scratch.
130   const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
131
132   Vector<Length> newGlyphsPerCharacter;
133   Length* glyphsPerCharacterBuffer = NULL;
134
135   // 1) Reserve some space for the glyphs per character to avoid reallocations.
136   if( updateCurrentBuffer )
137   {
138     newGlyphsPerCharacter.Resize( numberOfCharacters );
139     glyphsPerCharacterBuffer = newGlyphsPerCharacter.Begin();
140   }
141   else
142   {
143     mGlyphsPerCharacter.Resize( numberOfCharacters );
144     glyphsPerCharacterBuffer = mGlyphsPerCharacter.Begin() + startIndex;
145   }
146
147   // 2) Traverse the glyphs and set the number of glyphs per character.
148
149   // The glyph index.
150   const GlyphIndex glyphIndex = updateCurrentBuffer  ? *( mCharactersToGlyph.Begin() + startIndex ) : 0u;
151   Length traversedCharacters = 0;
152
153   // The number of 'characters per glyph' equal to zero.
154   Length zeroCharactersPerGlyph = 0u;
155
156   for( Vector<Length>::ConstIterator it = mCharactersPerGlyph.Begin() + glyphIndex,
157          endIt = mCharactersPerGlyph.End();
158        ( it != endIt ) && ( traversedCharacters < numberOfCharacters );
159        ++it )
160   {
161     const Length numberOfCharactersPerGlyph = *it;
162     traversedCharacters += numberOfCharactersPerGlyph;
163
164     // Set the glyphs per character.
165     if( 0u == numberOfCharactersPerGlyph )
166     {
167       ++zeroCharactersPerGlyph;
168     }
169     else
170     {
171       const Length numberOfZeroGlyphsPerCharacter = ( numberOfCharactersPerGlyph - 1u );
172       for( Length zeroIndex = 0u; zeroIndex < numberOfZeroGlyphsPerCharacter; ++zeroIndex )
173       {
174         *glyphsPerCharacterBuffer = 0u;
175
176         // Point to the next position in the buffer.
177         ++glyphsPerCharacterBuffer;
178       }
179
180       *glyphsPerCharacterBuffer = zeroCharactersPerGlyph + 1u;
181
182       zeroCharactersPerGlyph = 0u;
183
184       // Point to the next position in the buffer.
185       ++glyphsPerCharacterBuffer;
186     }
187   }
188
189   // If the glyphs per character buffer is updated, it needs to be inserted in the model.
190   if( updateCurrentBuffer )
191   {
192     mGlyphsPerCharacter.Insert( mGlyphsPerCharacter.Begin() + startIndex,
193                                 newGlyphsPerCharacter.Begin(),
194                                 newGlyphsPerCharacter.End() );
195   }
196 }
197
198 void VisualModel::GetGlyphs( GlyphInfo* glyphs,
199                              GlyphIndex glyphIndex,
200                              Length numberOfGlyphs ) const
201 {
202   memcpy( glyphs, mGlyphs.Begin() + glyphIndex, numberOfGlyphs * sizeof( GlyphInfo ) );
203 }
204
205 void VisualModel::GetGlyphPositions( Vector2* glyphPositions,
206                                      GlyphIndex glyphIndex,
207                                      Length numberOfGlyphs ) const
208 {
209   memcpy( glyphPositions, mGlyphPositions.Begin() + glyphIndex, numberOfGlyphs * sizeof( Vector2 ) );
210 }
211
212 void VisualModel::GetNumberOfLines( GlyphIndex glyphIndex,
213                                     Length numberOfGlyphs,
214                                     LineIndex& firstLine,
215                                     Length& numberOfLines ) const
216 {
217   // Initialize the number of lines and the first line.
218   firstLine = 0u;
219   numberOfLines = 0u;
220   bool firstLineFound = false;
221
222   const GlyphIndex lastGlyphIndex = glyphIndex + numberOfGlyphs;
223
224   // Traverse the lines and count those lines within the range of glyphs.
225   for( Vector<LineRun>::ConstIterator it = mLines.Begin(),
226          endIt = mLines.End();
227        it != endIt;
228        ++it )
229   {
230     const LineRun& line = *it;
231
232     if( ( line.glyphRun.glyphIndex + line.glyphRun.numberOfGlyphs > glyphIndex ) &&
233         ( lastGlyphIndex > line.glyphRun.glyphIndex ) )
234     {
235       firstLineFound = true;
236       ++numberOfLines;
237     }
238     else if( lastGlyphIndex <= line.glyphRun.glyphIndex )
239     {
240       // nothing else to do.
241       break;
242     }
243
244     if( !firstLineFound )
245     {
246       ++firstLine;
247     }
248   }
249 }
250
251 void VisualModel::GetLinesOfGlyphRange( LineRun* lines,
252                                         GlyphIndex glyphIndex,
253                                         Length numberOfGlyphs ) const
254 {
255   LineIndex firstLine = 0u;
256   Length numberOfLines = 0u;
257
258   GetNumberOfLines( glyphIndex,
259                     numberOfGlyphs,
260                     firstLine,
261                     numberOfLines );
262
263   memcpy( lines, mLines.Begin() + firstLine, numberOfLines * sizeof( LineRun ) );
264 }
265
266 LineIndex VisualModel::GetLineOfCharacter( CharacterIndex characterIndex )
267 {
268   // 1) Check first in the cached line.
269
270   const LineRun& lineRun = *( mLines.Begin() + mCachedLineIndex );
271
272   if( ( lineRun.characterRun.characterIndex <= characterIndex ) &&
273       ( characterIndex < lineRun.characterRun.characterIndex + lineRun.characterRun.numberOfCharacters ) )
274   {
275     return mCachedLineIndex;
276   }
277
278   // 2) Is not in the cached line. Check in the other lines.
279
280   LineIndex index = characterIndex < lineRun.characterRun.characterIndex ? 0u : mCachedLineIndex + 1u;
281
282   for( Vector<LineRun>::ConstIterator it = mLines.Begin() + index,
283          endIt = mLines.End();
284        it != endIt;
285        ++it, ++index )
286   {
287     const LineRun& lineRun = *it;
288
289     if( characterIndex < lineRun.characterRun.characterIndex + lineRun.characterRun.numberOfCharacters )
290     {
291       mCachedLineIndex = index;
292       break;
293     }
294   }
295
296   return index;
297 }
298
299 void VisualModel::GetUnderlineRuns( GlyphRun* underlineRuns,
300                                     UnderlineRunIndex index,
301                                     Length numberOfRuns ) const
302 {
303   memcpy( underlineRuns,
304           mUnderlineRuns.Begin() + index,
305           numberOfRuns * sizeof( GlyphRun ) );
306 }
307
308 void VisualModel::SetNaturalSize( const Vector2& size  )
309 {
310   mNaturalSize = size;
311 }
312
313 const Vector2& VisualModel::GetNaturalSize() const
314 {
315   return mNaturalSize;
316 }
317
318 void VisualModel::SetLayoutSize( const Vector2& size )
319 {
320   mLayoutSize = size;
321 }
322
323 const Vector2& VisualModel::GetLayoutSize() const
324 {
325   return mLayoutSize;
326 }
327
328 void VisualModel::SetTextColor( const Vector4& textColor )
329 {
330   mTextColor = textColor;
331
332   if ( !mUnderlineColorSet )
333   {
334     mUnderlineColor = textColor;
335   }
336 }
337
338 void VisualModel::SetShadowOffset( const Vector2& shadowOffset )
339 {
340   mShadowOffset = shadowOffset;
341 }
342
343 void VisualModel::SetShadowColor( const Vector4& shadowColor )
344 {
345   mShadowColor = shadowColor;
346 }
347
348 void VisualModel::SetUnderlineColor( const Vector4& color )
349 {
350   mUnderlineColor = color;
351   mUnderlineColorSet = true;
352 }
353
354 void VisualModel::SetUnderlineEnabled( bool enabled )
355 {
356   mUnderlineEnabled = enabled;
357 }
358
359 void VisualModel::SetUnderlineHeight( float height )
360 {
361   mUnderlineHeight = height;
362 }
363
364 const Vector4& VisualModel::GetTextColor() const
365 {
366   return mTextColor;
367 }
368
369 const Vector2& VisualModel::GetShadowOffset() const
370 {
371   return mShadowOffset;
372 }
373
374 const Vector4& VisualModel::GetShadowColor() const
375 {
376   return mShadowColor;
377 }
378
379 const Vector4& VisualModel::GetUnderlineColor() const
380 {
381   return mUnderlineColor;
382 }
383
384 bool VisualModel::IsUnderlineEnabled() const
385 {
386   return mUnderlineEnabled;
387 }
388
389 float VisualModel::GetUnderlineHeight() const
390 {
391   return mUnderlineHeight;
392 }
393
394 void VisualModel::ClearCaches()
395 {
396   mCachedLineIndex = 0u;
397 }
398
399 VisualModel::~VisualModel()
400 {
401 }
402
403 VisualModel::VisualModel()
404 : mGlyphs(),
405   mGlyphsToCharacters(),
406   mCharactersToGlyph(),
407   mCharactersPerGlyph(),
408   mGlyphsPerCharacter(),
409   mGlyphPositions(),
410   mLines(),
411   mTextColor( Color::BLACK ),
412   mShadowColor( Color::BLACK ),
413   mUnderlineColor( Color::BLACK ),
414   mShadowOffset( Vector2::ZERO ),
415   mUnderlineHeight( 0.0f ),
416   mNaturalSize(),
417   mLayoutSize(),
418   mCachedLineIndex( 0u ),
419   mUnderlineEnabled( false ),
420   mUnderlineColorSet( false )
421 {
422 }
423
424 } // namespace Text
425
426 } // namespace Toolkit
427
428 } // namespace Dali