Disable pixel alignment while scrolling the text
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / bidirectional-support.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 // FILE HEADER
19 #include <dali-toolkit/internal/text/bidirectional-support.h>
20
21 // EXTERNAL INCLUDES
22 #include <memory.h>
23 #include <dali/devel-api/text-abstraction/bidirectional-support.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Text
32 {
33
34 namespace
35 {
36
37 /**
38  * @brief Get the lines of a paragraph.
39  *
40  * @param[in] paragraphInfo The paragraph.
41  * @param[in] lines The lines.
42  * @param[in] lineIndex Index pointing the first line to be checked.
43  * @param[out] firstLine Index to the first line of the paragraph.
44  * @param[out] numberOfLines The number of lines.
45  */
46 void GetLines( const BidirectionalParagraphInfoRun& paragraphInfo,
47                const Vector<LineRun>& lines,
48                unsigned int lineIndex,
49                unsigned int& firstLine,
50                unsigned int& numberOfLines )
51 {
52   firstLine = lineIndex;
53   numberOfLines = 0u;
54
55   const CharacterIndex lastCharacterIndex = paragraphInfo.characterRun.characterIndex + paragraphInfo.characterRun.numberOfCharacters;
56   bool firstLineFound = false;
57
58   for( Vector<LineRun>::ConstIterator it = lines.Begin() + lineIndex,
59          endIt = lines.End();
60        it != endIt;
61        ++it )
62   {
63     const LineRun& line = *it;
64
65     if( ( line.characterRun.characterIndex + line.characterRun.numberOfCharacters > paragraphInfo.characterRun.characterIndex ) &&
66         ( lastCharacterIndex > line.characterRun.characterIndex ) )
67     {
68       firstLineFound = true;
69       ++numberOfLines;
70     }
71     else if( lastCharacterIndex <= line.characterRun.characterIndex )
72     {
73       // nothing else to do.
74       break;
75     }
76
77     if( !firstLineFound )
78     {
79       ++firstLine;
80     }
81   }
82 }
83
84 } // namespace
85
86 void SetBidirectionalInfo( const Vector<Character>& text,
87                            const Vector<ScriptRun>& scripts,
88                            const Vector<LineBreakInfo>& lineBreakInfo,
89                            CharacterIndex startIndex,
90                            Length numberOfCharacters,
91                            Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo )
92 {
93   // Find where to insert the new paragraphs.
94   BidirectionalRunIndex bidiInfoIndex = 0u;
95   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
96          endIt = bidirectionalInfo.End();
97        it != endIt;
98        ++it )
99   {
100     const BidirectionalParagraphInfoRun& run = *it;
101
102     if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
103     {
104       // Found where to insert the bidi info.
105       break;
106     }
107     ++bidiInfoIndex;
108   }
109
110   // Traverse the script runs. If there is one with a right to left script, create the bidirectional info for the paragraph containing that script is needed.
111   // From the bidirectional point of view, a paragraph is the piece of text between two LINE_MUST_BREAK.
112
113   // Index pointing the first character of the current paragraph.
114   CharacterIndex paragraphCharacterIndex = startIndex;
115
116   // Pointer to the text buffer.
117   const Character* textBuffer = text.Begin();
118
119   // Pointer to the line break info buffer.
120   const LineBreakInfo* lineBreakInfoBuffer = lineBreakInfo.Begin();
121
122   // Handle to the bidirectional info module in text-abstraction.
123   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
124
125   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
126
127   bool hasRightToLeftScript = false;
128
129   for( Vector<ScriptRun>::ConstIterator it = scripts.Begin(),
130          endIt = scripts.End();
131        it != endIt;
132        ++it )
133   {
134     const ScriptRun& scriptRun = *it;
135     const CharacterIndex lastScriptRunIndex = scriptRun.characterRun.characterIndex + scriptRun.characterRun.numberOfCharacters - 1u;
136
137     if( startIndex > lastScriptRunIndex )
138     {
139       // Skip the run as it has already been processed.
140       continue;
141     }
142
143     if( lastCharacter <= scriptRun.characterRun.characterIndex )
144     {
145       // Do not get bidirectional info beyond startIndex + numberOfCharacters.
146       break;
147     }
148
149     if( !hasRightToLeftScript && TextAbstraction::IsRightToLeftScript( scriptRun.script ) )
150     {
151       // The script is right to left.
152       hasRightToLeftScript = true;
153     }
154
155     if( TextAbstraction::LINE_MUST_BREAK == *( lineBreakInfoBuffer + lastScriptRunIndex ) )
156     {
157       // A new paragraph has been found.
158
159       if( hasRightToLeftScript )
160       {
161         // The Bidirectional run must have the same number of characters than the paragraph.
162         BidirectionalParagraphInfoRun bidirectionalRun;
163         bidirectionalRun.characterRun.characterIndex = paragraphCharacterIndex;
164         bidirectionalRun.characterRun.numberOfCharacters = ( lastScriptRunIndex - paragraphCharacterIndex ) + 1u; // The must break character is part of the paragrah.
165
166         // Create the bidirectional info for the whole paragraph and store the index to the table with this info in the run.
167         bidirectionalRun.bidirectionalInfoIndex = bidirectionalSupport.CreateInfo( textBuffer + bidirectionalRun.characterRun.characterIndex,
168                                                                                    bidirectionalRun.characterRun.numberOfCharacters );
169
170         bidirectionalInfo.Insert( bidirectionalInfo.Begin() + bidiInfoIndex, bidirectionalRun );
171         ++bidiInfoIndex;
172       }
173
174       // Point to the next paragraph.
175       paragraphCharacterIndex = lastScriptRunIndex + 1u;
176
177       // Reset whether there is a right to left script.
178       hasRightToLeftScript = false;
179     }
180   }
181
182   // Update indices of the bidi runs.
183   for( Vector<BidirectionalParagraphInfoRun>::Iterator it = bidirectionalInfo.Begin() + bidiInfoIndex,
184          endIt = bidirectionalInfo.End();
185        it != endIt;
186        ++it )
187   {
188     BidirectionalParagraphInfoRun& run = *it;
189
190     run.characterRun.characterIndex += numberOfCharacters;
191   }
192 }
193
194 void ReorderLines( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
195                    CharacterIndex startIndex,
196                    Length numberOfCharacters,
197                    Vector<LineRun>& lineRuns,
198                    Vector<BidirectionalLineInfoRun>& lineInfoRuns )
199 {
200   // Find where to insert the new paragraphs.
201   BidirectionalLineRunIndex bidiLineInfoIndex = 0u;
202   for( Vector<BidirectionalLineInfoRun>::ConstIterator it = lineInfoRuns.Begin(),
203          endIt = lineInfoRuns.End();
204        it != endIt;
205        ++it )
206   {
207     const BidirectionalLineInfoRun& run = *it;
208
209     if( startIndex < run.characterRun.characterIndex + run.characterRun.numberOfCharacters )
210     {
211       // Found where to insert the bidi line info.
212       break;
213     }
214     ++bidiLineInfoIndex;
215   }
216
217   // Handle to the bidirectional info module in text-abstraction.
218   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
219
220   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
221
222   // Keep an index to the first line to be checked if it's contained inside the paragraph.
223   // Avoids check the lines from the beginning for each paragraph.
224   unsigned int lineIndex = 0u;
225
226   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
227          endIt = bidirectionalInfo.End();
228        it != endIt;
229        ++it )
230   {
231     const BidirectionalParagraphInfoRun& paragraphInfo = *it;
232
233     if( paragraphInfo.characterRun.characterIndex < startIndex )
234     {
235       // Do not process, the paragraph has already been processed.
236       continue;
237     }
238
239     if( lastCharacter <= paragraphInfo.characterRun.characterIndex )
240     {
241       // Do not process paragraphs beyond startIndex + numberOfCharacters.
242       break;
243     }
244
245     const CharacterDirection direction = bidirectionalSupport.GetParagraphDirection( paragraphInfo.bidirectionalInfoIndex );
246
247     // Get the lines for this paragraph.
248     unsigned int firstLine = 0u;
249     unsigned int numberOfLines = 0u;
250
251     // Get an index to the first line and the number of lines of the current paragraph.
252     GetLines( paragraphInfo,
253               lineRuns,
254               lineIndex,
255               firstLine,
256               numberOfLines );
257
258     lineIndex = firstLine + numberOfLines;
259
260     // Traverse the lines and reorder them
261     for( Vector<LineRun>::Iterator lineIt = lineRuns.Begin() + firstLine,
262            endLineIt = lineRuns.Begin() + firstLine + numberOfLines;
263            lineIt != endLineIt;
264          ++lineIt )
265     {
266       LineRun& line = *lineIt;
267
268       // Sets the paragraph's direction.
269       line.direction = direction;
270
271       // Creates a bidirectional info for the line run.
272       BidirectionalLineInfoRun lineInfoRun;
273       lineInfoRun.characterRun.characterIndex = line.characterRun.characterIndex;
274       lineInfoRun.characterRun.numberOfCharacters = line.characterRun.numberOfCharacters;
275       lineInfoRun.direction = direction;
276
277       // Allocate space for the conversion maps.
278       // The memory is freed after the visual to logical to visual conversion tables are built in the logical model.
279       lineInfoRun.visualToLogicalMap = reinterpret_cast<CharacterIndex*>( malloc( line.characterRun.numberOfCharacters * sizeof( CharacterIndex ) ) );
280
281       if( NULL != lineInfoRun.visualToLogicalMap )
282       {
283         // Reorders the line.
284         bidirectionalSupport.Reorder( paragraphInfo.bidirectionalInfoIndex,
285                                       line.characterRun.characterIndex - paragraphInfo.characterRun.characterIndex,
286                                       line.characterRun.numberOfCharacters,
287                                       lineInfoRun.visualToLogicalMap );
288       }
289
290       // Push the run into the vector.
291       lineInfoRuns.Insert( lineInfoRuns.Begin() + bidiLineInfoIndex, lineInfoRun );
292       ++bidiLineInfoIndex;
293     }
294   }
295
296   // Update indices of the bidi runs.
297   for( Vector<BidirectionalLineInfoRun>::Iterator it = lineInfoRuns.Begin() + bidiLineInfoIndex,
298          endIt = lineInfoRuns.End();
299        it != endIt;
300        ++it )
301   {
302     BidirectionalLineInfoRun& run = *it;
303
304     run.characterRun.characterIndex += numberOfCharacters;
305   }
306 }
307
308 bool GetMirroredText( const Vector<Character>& text,
309                       const Vector<CharacterDirection>& directions,
310                       const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
311                       CharacterIndex startIndex,
312                       Length numberOfCharacters,
313                       Vector<Character>& mirroredText )
314 {
315   bool hasTextMirrored = false;
316
317   // Handle to the bidirectional info module in text-abstraction.
318   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
319
320   mirroredText = text;
321
322   Character* mirroredTextBuffer = mirroredText.Begin();
323   CharacterDirection* directionsBuffer = directions.Begin();
324
325   CharacterIndex index = startIndex;
326   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
327
328   // Traverse the paragraphs and mirror the right to left ones.
329   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
330          endIt = bidirectionalInfo.End();
331        it != endIt;
332        ++it )
333   {
334     const BidirectionalParagraphInfoRun& paragraph = *it;
335
336     if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
337     {
338       // Skip the paragraph as it has already been processed.
339       continue;
340     }
341
342     if( lastCharacter <= paragraph.characterRun.characterIndex )
343     {
344       // Do not get mirror characters beyond startIndex + numberOfCharacters.
345       break;
346     }
347
348     index += paragraph.characterRun.numberOfCharacters;
349     const bool tmpMirrored = bidirectionalSupport.GetMirroredText( mirroredTextBuffer + paragraph.characterRun.characterIndex,
350                                                                    directionsBuffer + paragraph.characterRun.characterIndex,
351                                                                    paragraph.characterRun.numberOfCharacters );
352
353     hasTextMirrored = hasTextMirrored || tmpMirrored;
354   }
355
356   return hasTextMirrored;
357 }
358
359 void GetCharactersDirection( const Vector<BidirectionalParagraphInfoRun>& bidirectionalInfo,
360                              Length totalNumberOfCharacters,
361                              CharacterIndex startIndex,
362                              Length numberOfCharacters,
363                              Vector<CharacterDirection>& directions )
364 {
365   // Handle to the bidirectional info module in text-abstraction.
366   TextAbstraction::BidirectionalSupport bidirectionalSupport = TextAbstraction::BidirectionalSupport::Get();
367
368   // Resize the vector.
369   directions.Resize( totalNumberOfCharacters );
370
371   // Whether the current buffer is being updated or is set from scratch.
372   const bool updateCurrentBuffer = numberOfCharacters < totalNumberOfCharacters;
373
374   CharacterDirection* directionsBuffer = NULL;
375   Vector<CharacterDirection> newDirections;
376
377   if( updateCurrentBuffer )
378   {
379     newDirections.Resize( numberOfCharacters );
380     directionsBuffer = newDirections.Begin();
381   }
382   else
383   {
384     directionsBuffer = directions.Begin();
385   }
386
387   const CharacterIndex lastCharacter = startIndex + numberOfCharacters;
388   CharacterIndex index = startIndex;
389
390   for( Vector<BidirectionalParagraphInfoRun>::ConstIterator it = bidirectionalInfo.Begin(),
391          endIt = bidirectionalInfo.End();
392        it != endIt;
393        ++it )
394   {
395     const BidirectionalParagraphInfoRun& paragraph = *it;
396
397     if( index >= paragraph.characterRun.characterIndex + paragraph.characterRun.numberOfCharacters )
398     {
399       // Skip the paragraph as it has already been processed.
400       continue;
401     }
402
403     if( lastCharacter <= paragraph.characterRun.characterIndex )
404     {
405       // Do not get the character directions beyond startIndex + numberOfCharacters.
406       break;
407     }
408
409     // Set the directions of any previous left to right characters.
410     const Length numberOfLeftToRightCharacters = paragraph.characterRun.characterIndex - index;
411     if( numberOfLeftToRightCharacters > 0u )
412     {
413       memset( directionsBuffer + index - startIndex, false, numberOfLeftToRightCharacters * sizeof( bool ) );
414     }
415
416     // Set the directions of the bidirectional text.
417     bidirectionalSupport.GetCharactersDirection( paragraph.bidirectionalInfoIndex,
418                                                  directionsBuffer + paragraph.characterRun.characterIndex - startIndex,
419                                                  paragraph.characterRun.numberOfCharacters );
420
421     // Update the index.
422     index += paragraph.characterRun.numberOfCharacters + numberOfLeftToRightCharacters;
423   }
424
425   // Fills with left to right those paragraphs without right to left characters.
426   memset( directionsBuffer + index - startIndex, false, ( lastCharacter - index ) * sizeof( bool ) );
427
428   // If the direction info is updated, it needs to be inserted in the model.
429   if( updateCurrentBuffer )
430   {
431     // Insert the directions in the given buffer.
432     directions.Insert( directions.Begin() + startIndex,
433                        newDirections.Begin(),
434                        newDirections.End() );
435     directions.Resize( totalNumberOfCharacters );
436   }
437 }
438
439 } // namespace Text
440
441 } // namespace Toolkit
442
443 } // namespace Dali