b91b285302b014658c052454445e9e4183c30631
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / logical-model.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/logical-model.h>
20
21 // EXTERNAL INCLUDES
22 #include <memory.h>
23
24 // INTERNAL INCLUDES
25 #include <dali-toolkit/internal/text/bidirectional-line-info-run.h>
26 #include <dali-toolkit/internal/text/bidirectional-paragraph-info-run.h>
27 #include <dali-toolkit/internal/text/font-run.h>
28 #include <dali-toolkit/internal/text/script-run.h>
29
30 namespace Dali
31 {
32
33 namespace Toolkit
34 {
35
36 namespace Text
37 {
38 /**
39  * @brief caches some temporary values of the GetNumberOfScriptRuns( characterIndex, numberOfCharacters )
40  * operation and the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) as they are going to be
41  * used in the GetScriptRuns() and the GetFontRuns() calls.
42  */
43 struct GetRunCache
44 {
45   CharacterIndex characterIndex;     ///< The character index.
46   Length         numberOfCharacters; ///< The number of characters.
47   Length         firstRun;           ///< Index to the first run.
48   Length         numberOfRuns;       ///< The number of runs.
49 };
50
51 struct LogicalModel::Impl
52 {
53   Vector<Character>                     mText;
54   Vector<ScriptRun>                     mScriptRuns;
55   Vector<FontRun>                       mFontRuns;
56   Vector<LineBreakInfo>                 mLineBreakInfo;
57   Vector<WordBreakInfo>                 mWordBreakInfo;
58   Vector<BidirectionalParagraphInfoRun> mBidirectionalParagraphInfo;
59
60   Vector<BidirectionalLineInfoRun>      mBidirectionalLineInfo;
61   Vector<CharacterIndex>                mLogicalToVisualMap; ///< Bidirectional logical to visual conversion table.
62   Vector<CharacterIndex>                mVisualToLogicalMap; ///< Bidirectional visual to logical conversion table.
63
64   GetRunCache                           mGetScriptCache;             ///< Caches the GetNumberOfScriptRuns( characterIndex, numberOfCharacters ) operation.
65   GetRunCache                           mGetFontCache;               ///< Caches the GetNumberOfFontRuns( characterIndex, numberOfCharacters ) operation.
66   GetRunCache                           mGetBidirectionalCache;      ///< Caches the GetNumberOfBidirectionalInfoRuns( characterIndex, numberOfCharacters ) operation.
67 };
68
69 LogicalModelPtr LogicalModel::New()
70 {
71   return LogicalModelPtr( new LogicalModel() );
72 }
73
74 void LogicalModel::SetText( const Character* const text,
75                             Length numberOfCharacters )
76 {
77   Vector<Character>& modelText = mImpl->mText;
78
79   if( 0u == numberOfCharacters )
80   {
81     modelText.Clear();
82   }
83   else
84   {
85     modelText.Resize( numberOfCharacters );
86     memcpy( modelText.Begin(), text, numberOfCharacters * sizeof( Character ) );
87   }
88 }
89
90 Length LogicalModel::GetNumberOfCharacters() const
91 {
92   return mImpl->mText.Count();
93 }
94
95 void LogicalModel::GetText( Character* text,
96                             CharacterIndex characterIndex,
97                             Length numberOfCharacters ) const
98 {
99   Vector<Character>& modelText = mImpl->mText;
100   memcpy( text, modelText.Begin() + characterIndex, numberOfCharacters * sizeof( Character ) );
101 }
102
103 Character LogicalModel::GetCharacter( CharacterIndex characterIndex ) const
104 {
105   return mImpl->mText[characterIndex];
106 }
107
108 void LogicalModel::SetScripts( const ScriptRun* const scripts,
109                                Length numberOfRuns )
110 {
111   Vector<ScriptRun>& scriptRuns = mImpl->mScriptRuns;
112
113   if( 0u == numberOfRuns )
114   {
115     scriptRuns.Clear();
116   }
117   else
118   {
119     scriptRuns.Resize( numberOfRuns );
120     memcpy( scriptRuns.Begin(), scripts, numberOfRuns * sizeof( ScriptRun ) );
121   }
122
123   mImpl->mGetScriptCache.characterIndex = 0u;
124   mImpl->mGetScriptCache.numberOfCharacters = 0u;
125   mImpl->mGetScriptCache.firstRun = 0u;
126   mImpl->mGetScriptCache.numberOfRuns = 0u;
127 }
128
129 Length LogicalModel::GetNumberOfScriptRuns( CharacterIndex characterIndex,
130                                             Length numberOfCharacters ) const
131 {
132   GetRunCache& scriptCache = mImpl->mGetScriptCache;
133
134   // Set the character index and the number of characters into the cache.
135   scriptCache.characterIndex = characterIndex;
136   scriptCache.numberOfCharacters = numberOfCharacters;
137
138   if( ( 0u == characterIndex ) &&
139       ( mImpl->mText.Count() == numberOfCharacters ) )
140   {
141     scriptCache.firstRun = 0u;
142     scriptCache.numberOfRuns = mImpl->mScriptRuns.Count();
143     return scriptCache.numberOfRuns;
144   }
145
146   // Initialize the number of scripts and the index to the first script.
147   scriptCache.firstRun = 0u;
148   scriptCache.numberOfRuns = 0;
149   bool firstScriptFound = false;
150
151   const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
152   const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
153
154   // Traverse the scripts and count those scripts within the range of characters.
155   for( Vector<ScriptRun>::ConstIterator it = modelScripts.Begin(),
156          endIt = modelScripts.End();
157        it != endIt;
158        ++it )
159   {
160     const ScriptRun& script = *it;
161
162     if( ( script.characterRun.characterIndex + script.characterRun.numberOfCharacters > characterIndex ) &&
163         ( lastCharacterIndex > script.characterRun.characterIndex ) )
164     {
165       firstScriptFound = true;
166       ++scriptCache.numberOfRuns;
167     }
168     else if( lastCharacterIndex <= script.characterRun.characterIndex )
169     {
170       // nothing else to do.
171       break;
172     }
173
174     if( !firstScriptFound )
175     {
176       ++scriptCache.firstRun;
177     }
178   }
179
180   return scriptCache.numberOfRuns;
181 }
182
183 void LogicalModel::GetScriptRuns( ScriptRun* scriptRuns,
184                                   CharacterIndex characterIndex,
185                                   Length numberOfCharacters ) const
186 {
187   const Vector<ScriptRun>& modelScripts = mImpl->mScriptRuns;
188   GetRunCache& scriptCache = mImpl->mGetScriptCache;
189
190   if( ( characterIndex != scriptCache.characterIndex ) ||
191       ( numberOfCharacters != scriptCache.numberOfCharacters ) )
192   {
193     GetNumberOfScriptRuns( characterIndex,
194                            numberOfCharacters );
195   }
196
197   memcpy( scriptRuns, modelScripts.Begin() + scriptCache.firstRun, scriptCache.numberOfRuns * sizeof( ScriptRun ) );
198 }
199
200 Script LogicalModel::GetScript( CharacterIndex characterIndex ) const
201 {
202   // If this operation is too slow, consider a binary search.
203
204   for( Length index = 0u, length = mImpl->mScriptRuns.Count(); index < length; ++index )
205   {
206     const ScriptRun* const scriptRun = mImpl->mScriptRuns.Begin() + index;
207
208     if( ( scriptRun->characterRun.characterIndex <= characterIndex ) &&
209         ( characterIndex < scriptRun->characterRun.characterIndex + scriptRun->characterRun.numberOfCharacters ) )
210     {
211       return scriptRun->script;
212     }
213   }
214
215   return TextAbstraction::UNKNOWN;
216 }
217
218 void LogicalModel::SetFonts( const FontRun* const fonts,
219                              Length numberOfRuns )
220 {
221   Vector<FontRun>& fontRuns = mImpl->mFontRuns;
222
223   if( 0u == numberOfRuns )
224   {
225     fontRuns.Clear();
226   }
227   else
228   {
229     fontRuns.Resize( numberOfRuns );
230     memcpy( fontRuns.Begin(), fonts, numberOfRuns * sizeof( FontRun ) );
231   }
232
233   mImpl->mGetFontCache.characterIndex = 0u;
234   mImpl->mGetFontCache.numberOfCharacters = 0u;
235   mImpl->mGetFontCache.firstRun = 0u;
236   mImpl->mGetFontCache.numberOfRuns = 0u;
237 }
238
239 Length LogicalModel::GetNumberOfFontRuns( CharacterIndex characterIndex,
240                                           Length numberOfCharacters ) const
241 {
242   GetRunCache& fontCache = mImpl->mGetFontCache;
243
244   // Set the character index and the number of characters into the cache.
245   fontCache.characterIndex = characterIndex;
246   fontCache.numberOfCharacters = numberOfCharacters;
247
248   if( ( 0u == characterIndex ) &&
249       ( mImpl->mText.Count() == numberOfCharacters ) )
250   {
251     fontCache.firstRun = 0u;
252     fontCache.numberOfRuns = mImpl->mFontRuns.Count();
253     return fontCache.numberOfRuns;
254   }
255
256   // Initialize the number of fonts and the index to the first font.
257   fontCache.firstRun = 0u;
258   fontCache.numberOfRuns = 0;
259   bool firstFontFound = false;
260
261   const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
262   const CharacterIndex lastCharacterIndex = characterIndex + numberOfCharacters;
263
264   // Traverse the fonts and count those fonts within the range of characters.
265   for( Vector<FontRun>::ConstIterator it = modelFonts.Begin(),
266          endIt = modelFonts.End();
267        it != endIt;
268        ++it )
269   {
270     const FontRun& font = *it;
271
272     if( ( font.characterRun.characterIndex + font.characterRun.numberOfCharacters > characterIndex ) &&
273         ( characterIndex + numberOfCharacters > font.characterRun.characterIndex ) )
274     {
275       firstFontFound = true;
276       ++fontCache.numberOfRuns;
277     }
278     else if( lastCharacterIndex <= font.characterRun.characterIndex )
279     {
280       // nothing else to do.
281       break;
282     }
283
284     if( !firstFontFound )
285     {
286       ++fontCache.firstRun;
287     }
288   }
289
290   return fontCache.numberOfRuns;
291 }
292
293 void LogicalModel::GetFontRuns( FontRun* fontRuns,
294                                 CharacterIndex characterIndex,
295                                 Length numberOfCharacters ) const
296 {
297   const Vector<FontRun>& modelFonts = mImpl->mFontRuns;
298   GetRunCache& fontCache = mImpl->mGetFontCache;
299
300   if( ( characterIndex != fontCache.characterIndex ) ||
301       ( numberOfCharacters != fontCache.numberOfCharacters ) )
302   {
303     GetNumberOfFontRuns( characterIndex,
304                          numberOfCharacters );
305   }
306
307   memcpy( fontRuns, modelFonts.Begin() + fontCache.firstRun, fontCache.numberOfRuns * sizeof( FontRun ) );
308 }
309
310 FontId LogicalModel::GetFont( CharacterIndex characterIndex ) const
311 {
312   for( Length index = 0u, length = mImpl->mFontRuns.Count(); index < length; ++index )
313   {
314     const FontRun* const fontRun = mImpl->mFontRuns.Begin() + index;
315
316     if( ( fontRun->characterRun.characterIndex <= characterIndex ) &&
317         ( characterIndex < fontRun->characterRun.characterIndex + fontRun->characterRun.numberOfCharacters ) )
318     {
319       return fontRun->fontId;
320     }
321   }
322
323   return 0u;
324 }
325
326 void LogicalModel::SetLineBreakInfo( const LineBreakInfo* const lineBreakInfo,
327                                      Length length )
328 {
329   Vector<LineBreakInfo>& modelLineBreakInfo = mImpl->mLineBreakInfo;
330
331   if( 0u == length )
332   {
333     modelLineBreakInfo.Clear();
334   }
335   else
336   {
337     modelLineBreakInfo.Resize( length );
338     memcpy( modelLineBreakInfo.Begin(), lineBreakInfo, length * sizeof( LineBreakInfo ) );
339   }
340 }
341
342 void LogicalModel::GetLineBreakInfo( LineBreakInfo* lineBreakInfo,
343                                      CharacterIndex characterIndex,
344                                      Length numberOfItems ) const
345 {
346   memcpy( lineBreakInfo, mImpl->mLineBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( LineBreakInfo ) );
347 }
348
349 LineBreakInfo LogicalModel::GetLineBreakInfo( CharacterIndex characterIndex ) const
350 {
351   return *( mImpl->mLineBreakInfo.Begin() + characterIndex );
352 }
353
354 void LogicalModel::SetWordBreakInfo( const WordBreakInfo* const wordBreakInfo,
355                                      Length length )
356 {
357   Vector<WordBreakInfo>& modelWordBreakInfo = mImpl->mWordBreakInfo;
358
359   if( 0u == length )
360   {
361     modelWordBreakInfo.Clear();
362   }
363   else
364   {
365     modelWordBreakInfo.Resize( length );
366     memcpy( modelWordBreakInfo.Begin(), wordBreakInfo, length * sizeof( WordBreakInfo ) );
367   }
368 }
369
370 void LogicalModel::GetWordBreakInfo( WordBreakInfo* wordBreakInfo,
371                                      CharacterIndex characterIndex,
372                                      Length numberOfItems ) const
373 {
374   memcpy( wordBreakInfo, mImpl->mWordBreakInfo.Begin() + characterIndex, numberOfItems * sizeof( WordBreakInfo ) );
375 }
376
377 WordBreakInfo LogicalModel::GetWordBreakInfo( CharacterIndex characterIndex ) const
378 {
379   return *( mImpl->mWordBreakInfo.Begin() + characterIndex );
380 }
381
382 void LogicalModel::SetBidirectionalInfo( const BidirectionalParagraphInfoRun* const bidirectionalInfo,
383                                          Length numberOfRuns )
384 {
385   Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
386
387   if( 0u == numberOfRuns )
388   {
389     modelBidirectionalParagraphInfo.Clear();
390   }
391   else
392   {
393     modelBidirectionalParagraphInfo.Resize( numberOfRuns );
394     memcpy( modelBidirectionalParagraphInfo.Begin(), bidirectionalInfo, numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
395   }
396
397   mImpl->mGetBidirectionalCache.characterIndex = 0u;
398   mImpl->mGetBidirectionalCache.numberOfCharacters = 0u;
399   mImpl->mGetBidirectionalCache.firstRun = 0u;
400   mImpl->mGetBidirectionalCache.numberOfRuns = 0u;
401 }
402
403 Length LogicalModel::GetNumberOfBidirectionalInfoRuns( CharacterIndex characterIndex,
404                                                        Length numberOfCharacters ) const
405 {
406   GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
407
408   // Set the character index and the number of characters into the cache.
409   bidiCache.characterIndex = characterIndex;
410   bidiCache.numberOfCharacters = numberOfCharacters;
411
412   if( ( 0u == characterIndex ) &&
413       ( mImpl->mText.Count() == numberOfCharacters ) )
414   {
415     bidiCache.firstRun = 0u;
416     bidiCache.numberOfRuns = mImpl->mBidirectionalParagraphInfo.Count();
417     return bidiCache.numberOfRuns;
418   }
419
420   // Initialize the number of bidi paragraphs and the index to the first paragraph.
421   bidiCache.firstRun = 0u;
422   bidiCache.numberOfRuns = 0;
423   bool firstParagraphFound = false;
424
425   const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
426
427   // Traverse the bidirectional paragraph info and count those bidi paragraphs within the range of characters.
428   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = modelBidirectionalParagraphInfo.Begin(),
429          endIt = modelBidirectionalParagraphInfo.End();
430        it != endIt;
431        ++it )
432   {
433     const BidirectionalParagraphInfoRun& bidi = *it;
434
435     if( ( bidi.characterRun.characterIndex + bidi.characterRun.numberOfCharacters > characterIndex ) &&
436         ( characterIndex + numberOfCharacters > bidi.characterRun.characterIndex ) )
437     {
438       firstParagraphFound = true;
439       ++bidiCache.numberOfRuns;
440     }
441
442     if( !firstParagraphFound )
443     {
444       ++bidiCache.firstRun;
445     }
446   }
447
448   return bidiCache.numberOfRuns;
449 }
450
451 void LogicalModel::GetBidirectionalInfo( BidirectionalParagraphInfoRun* bidirectionalInfo,
452                                          CharacterIndex characterIndex,
453                                          Length numberOfCharacters ) const
454 {
455   const Vector<BidirectionalParagraphInfoRun>& modelBidirectionalParagraphInfo = mImpl->mBidirectionalParagraphInfo;
456   GetRunCache& bidiCache = mImpl->mGetBidirectionalCache;
457
458   if( ( characterIndex != bidiCache.characterIndex ) ||
459       ( numberOfCharacters != bidiCache.numberOfCharacters ) )
460   {
461     GetNumberOfBidirectionalInfoRuns( characterIndex,
462                                       numberOfCharacters );
463   }
464
465   memcpy( bidirectionalInfo, modelBidirectionalParagraphInfo.Begin() + bidiCache.firstRun, bidiCache.numberOfRuns * sizeof( BidirectionalParagraphInfoRun ) );
466 }
467
468 void LogicalModel::GetCharacterDirections( CharacterDirection* directions,
469                                            CharacterIndex characterIndex,
470                                            Length numberOfCharacters ) const
471 {
472 }
473
474 CharacterDirection LogicalModel::GetCharacterDirection( CharacterIndex characterIndex ) const
475 {
476   return false;
477 }
478
479 void LogicalModel::SetVisualToLogicalMap( const BidirectionalLineInfoRun* const bidirectionalInfo,
480                                           Length numberOfRuns )
481 {
482   Vector<CharacterIndex>& modelVisualToLogicalMap = mImpl->mVisualToLogicalMap;
483   Vector<CharacterIndex>& modelLogicalToVisualMap = mImpl->mLogicalToVisualMap;
484
485   if( 0u == numberOfRuns )
486   {
487     modelVisualToLogicalMap.Clear();
488     modelLogicalToVisualMap.Clear();
489   }
490   else
491   {
492     const Length numberOfCharacters = mImpl->mText.Count();
493     modelVisualToLogicalMap.Resize( numberOfCharacters );
494     modelLogicalToVisualMap.Resize( numberOfCharacters );
495
496     CharacterIndex* modelVisualToLogicalMapBuffer = modelVisualToLogicalMap.Begin();
497     CharacterIndex* modelLogicalToVisualMapBuffer = modelLogicalToVisualMap.Begin();
498
499     CharacterIndex lastIndex = 0u;
500     for( unsigned int bidiIndex = 0u; bidiIndex < numberOfRuns; ++bidiIndex )
501     {
502       const BidirectionalLineInfoRun& bidiLineInfo = *( bidirectionalInfo + bidiIndex );
503
504       if( lastIndex < bidiLineInfo.characterRun.characterIndex )
505       {
506         // Fill with the identity.
507         for( ; lastIndex < bidiLineInfo.characterRun.characterIndex; ++lastIndex )
508         {
509           *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
510         }
511       }
512
513       // Fill the conversion table of the run.
514       for( CharacterIndex index = 0u;
515            index < bidiLineInfo.characterRun.numberOfCharacters;
516            ++index, ++lastIndex )
517       {
518         *( modelVisualToLogicalMapBuffer + lastIndex ) = bidiLineInfo.characterRun.characterIndex + *( bidiLineInfo.visualToLogicalMap + index );
519       }
520     }
521
522     // Complete with the identity if there are some left to right characters after the last right to left.
523     for( ; lastIndex < numberOfCharacters; ++lastIndex )
524     {
525       *( modelVisualToLogicalMapBuffer + lastIndex ) = lastIndex;
526     }
527
528     // Sets the logical to visual conversion map.
529     for( CharacterIndex index = 0u; index < numberOfCharacters; ++index )
530     {
531       *( modelLogicalToVisualMapBuffer + *( modelVisualToLogicalMapBuffer + index ) ) = index;
532     }
533   }
534 }
535
536 CharacterIndex LogicalModel::GetVisualCharacterIndex( CharacterIndex logicalCharacterIndex ) const
537 {
538   if( 0u == mImpl->mLogicalToVisualMap.Count() )
539   {
540     // If there is no logical to visual info is because the whole text is left to right.
541     // Return the identity.
542     return logicalCharacterIndex;
543   }
544
545   return *( mImpl->mLogicalToVisualMap.Begin() + logicalCharacterIndex );
546 }
547
548 CharacterIndex LogicalModel::GetLogicalCharacterIndex( CharacterIndex visualCharacterIndex ) const
549 {
550   if( 0u == mImpl->mVisualToLogicalMap.Count() )
551   {
552     // If there is no visual to logical info is because the whole text is left to right.
553     // Return the identity.
554     return visualCharacterIndex;
555   }
556
557   return *( mImpl->mVisualToLogicalMap.Begin() + visualCharacterIndex );
558 }
559
560 void LogicalModel::GetLogicalToVisualMap( CharacterIndex* logicalToVisualMap,
561                                           CharacterIndex characterIndex,
562                                           Length numberOfCharacters ) const
563 {
564   memcpy( logicalToVisualMap, mImpl->mLogicalToVisualMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
565 }
566
567 void LogicalModel::GetVisualToLogicalMap( CharacterIndex* visualToLogicalMap,
568                                           CharacterIndex characterIndex,
569                                           Length numberOfCharacters ) const
570 {
571   memcpy( visualToLogicalMap, mImpl->mVisualToLogicalMap.Begin() + characterIndex, numberOfCharacters * sizeof( CharacterIndex ) );
572 }
573
574 LogicalModel::~LogicalModel()
575 {
576   delete mImpl;
577 }
578
579 LogicalModel::LogicalModel()
580 : mImpl( NULL )
581 {
582   mImpl = new LogicalModel::Impl();
583 }
584
585 } // namespace Text
586
587 } // namespace Toolkit
588
589 } // namespace Dali