Merge "Fix for text renderer. Set the right size in order to cull the actor correctly...
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / text / text-view.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/text-view.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/math/vector2.h>
23 #include <dali/devel-api/text-abstraction/font-client.h>
24
25 namespace Dali
26 {
27
28 namespace Toolkit
29 {
30
31 namespace Text
32 {
33
34 struct View::Impl
35 {
36   VisualModelPtr mVisualModel;
37   TextAbstraction::FontClient mFontClient; ///< Handle to the font client.
38 };
39
40 View::View()
41 : mImpl( NULL )
42 {
43   mImpl = new View::Impl();
44
45   mImpl->mFontClient = TextAbstraction::FontClient::Get();
46 }
47
48 View::~View()
49 {
50   delete mImpl;
51 }
52
53 void View::SetVisualModel( VisualModelPtr visualModel )
54 {
55   mImpl->mVisualModel = visualModel;
56 }
57
58 const Vector2& View::GetControlSize() const
59 {
60   if ( mImpl->mVisualModel )
61   {
62     return mImpl->mVisualModel->mControlSize;
63   }
64
65   return Vector2::ZERO;
66 }
67
68 const Vector2& View::GetActualSize() const
69 {
70   if ( mImpl->mVisualModel )
71   {
72     return mImpl->mVisualModel->GetActualSize();
73   }
74
75   return Vector2::ZERO;
76 }
77
78 Length View::GetNumberOfGlyphs() const
79 {
80   if( mImpl->mVisualModel )
81   {
82     const VisualModel& model = *mImpl->mVisualModel;
83
84     const Length glyphCount = model.mGlyphs.Count();
85     const Length positionCount = model.mGlyphPositions.Count();
86
87     DALI_ASSERT_DEBUG( positionCount <= glyphCount && "Invalid glyph positions in Model" );
88
89     return (positionCount < glyphCount) ? positionCount : glyphCount;
90   }
91
92   return 0;
93 }
94
95 Length View::GetGlyphs( GlyphInfo* glyphs,
96                         Vector2* glyphPositions,
97                         Vector4* glyphColors,
98                         GlyphIndex glyphIndex,
99                         Length numberOfGlyphs ) const
100 {
101   Length numberOfLaidOutGlyphs = 0u;
102
103   if( mImpl->mVisualModel )
104   {
105     // If ellipsis is enabled, the number of glyphs the layout engine has laid out may be less than 'numberOfGlyphs'.
106     // Check the last laid out line to know if the layout engine elided some text.
107
108     const Length numberOfLines = mImpl->mVisualModel->mLines.Count();
109     if( numberOfLines > 0u )
110     {
111       const LineRun& lastLine = *( mImpl->mVisualModel->mLines.Begin() + ( numberOfLines - 1u ) );
112
113       // If ellipsis is enabled, calculate the number of laid out glyphs.
114       // Otherwise use the given number of glyphs.
115       if( lastLine.ellipsis )
116       {
117         numberOfLaidOutGlyphs = lastLine.glyphRun.glyphIndex + lastLine.glyphRun.numberOfGlyphs;
118       }
119       else
120       {
121         numberOfLaidOutGlyphs = numberOfGlyphs;
122       }
123
124       if( 0u < numberOfLaidOutGlyphs )
125       {
126         // Retrieve from the visual model the glyphs and positions.
127         mImpl->mVisualModel->GetGlyphs( glyphs,
128                                         glyphIndex,
129                                         numberOfLaidOutGlyphs );
130
131         mImpl->mVisualModel->GetGlyphPositions( glyphPositions,
132                                                 glyphIndex,
133                                                 numberOfLaidOutGlyphs );
134
135         // Set the colors.
136         const GlyphIndex lastLaidOutGlyphIndex = glyphIndex + numberOfLaidOutGlyphs;
137
138         for( Vector<ColorGlyphRun>::ConstIterator it = mImpl->mVisualModel->mColorRuns.Begin(),
139                endIt = mImpl->mVisualModel->mColorRuns.End();
140              it != endIt;
141              ++it )
142         {
143           const ColorGlyphRun& colorGlyphRun = *it;
144           const GlyphIndex lastGlyphIndex = colorGlyphRun.glyphRun.glyphIndex + colorGlyphRun.glyphRun.numberOfGlyphs;
145
146           if( ( colorGlyphRun.glyphRun.glyphIndex < lastLaidOutGlyphIndex ) &&
147               ( glyphIndex < lastGlyphIndex ) )
148           {
149             for( GlyphIndex index = glyphIndex < colorGlyphRun.glyphRun.glyphIndex ? colorGlyphRun.glyphRun.glyphIndex : glyphIndex,
150                    endIndex = lastLaidOutGlyphIndex < lastGlyphIndex ? lastLaidOutGlyphIndex : lastGlyphIndex;
151                  index < endIndex;
152                  ++index )
153             {
154               *( glyphColors + index - glyphIndex ) = colorGlyphRun.color;
155             }
156           }
157         }
158
159         // Get the lines for the given range of glyphs.
160         // The lines contain the alignment offset which needs to be added to the glyph's position.
161         LineIndex firstLine = 0u;
162         Length numberOfLines = 0u;
163         mImpl->mVisualModel->GetNumberOfLines( glyphIndex,
164                                                numberOfLaidOutGlyphs,
165                                                firstLine,
166                                                numberOfLines );
167
168         Vector<LineRun> lines;
169         lines.Resize( numberOfLines );
170         LineRun* lineBuffer = lines.Begin();
171
172         mImpl->mVisualModel->GetLinesOfGlyphRange( lineBuffer,
173                                                    glyphIndex,
174                                                    numberOfLaidOutGlyphs );
175
176         // Get the first line for the given glyph range.
177         LineIndex lineIndex = firstLine;
178         LineRun* line = lineBuffer + lineIndex;
179
180         // Index of the last glyph of the line.
181         GlyphIndex lastGlyphIndexOfLine = line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs - 1u;
182
183         // Add the alignment offset to the glyph's position.
184         for( Length index = 0u; index < numberOfLaidOutGlyphs; ++index )
185         {
186           ( *( glyphPositions + index ) ).x += line->alignmentOffset;
187
188           if( lastGlyphIndexOfLine == index )
189           {
190             // Get the next line.
191             ++lineIndex;
192
193             if( lineIndex < numberOfLines )
194             {
195               line = lineBuffer + lineIndex;
196               lastGlyphIndexOfLine = line->glyphRun.glyphIndex + line->glyphRun.numberOfGlyphs - 1u;
197             }
198           }
199         }
200
201         if( 1u == numberOfLaidOutGlyphs )
202         {
203           // not a point try to do ellipsis with only one laid out character.
204           return numberOfLaidOutGlyphs;
205         }
206
207         if( lastLine.ellipsis )
208         {
209           // firstPenX, penY and firstPenSet are used to position the ellipsis glyph if needed.
210           float firstPenX = 0.f; // Used if rtl text is elided.
211           float penY = 0.f;
212           bool firstPenSet = false;
213
214           // Add the ellipsis glyph.
215           bool inserted = false;
216           float removedGlypsWidth = 0.f;
217           Length numberOfRemovedGlyphs = 0u;
218           GlyphIndex index = numberOfLaidOutGlyphs - 1u;
219
220           // The ellipsis glyph has to fit in the place where the last glyph(s) is(are) removed.
221           while( !inserted )
222           {
223             const GlyphInfo& glyphToRemove = *( glyphs + index );
224
225             if( 0u != glyphToRemove.fontId )
226             {
227               // i.e. The font id of the glyph shaped from the '\n' character is zero.
228
229               // Need to reshape the glyph as the font may be different in size.
230               const GlyphInfo& ellipsisGlyph = mImpl->mFontClient.GetEllipsisGlyph( mImpl->mFontClient.GetPointSize( glyphToRemove.fontId ) );
231
232               if( !firstPenSet )
233               {
234                 const Vector2& position = *( glyphPositions + index );
235
236                 // Calculates the penY of the current line. It will be used to position the ellipsis glyph.
237                 penY = position.y + glyphToRemove.yBearing;
238
239                 // Calculates the first penX which will be used if rtl text is elided.
240                 firstPenX = position.x - glyphToRemove.xBearing;
241                 if( firstPenX < -ellipsisGlyph.xBearing )
242                 {
243                   // Avoids to exceed the bounding box when rtl text is elided.
244                   firstPenX = -ellipsisGlyph.xBearing;
245                 }
246
247                 removedGlypsWidth = -ellipsisGlyph.xBearing;
248
249                 firstPenSet = true;
250               }
251
252               removedGlypsWidth += std::min( glyphToRemove.advance, ( glyphToRemove.xBearing + glyphToRemove.width ) );
253
254               // Calculate the width of the ellipsis glyph and check if it fits.
255               const float ellipsisGlyphWidth = ellipsisGlyph.width + ellipsisGlyph.xBearing;
256               if( ellipsisGlyphWidth < removedGlypsWidth )
257               {
258                 GlyphInfo& glyphInfo = *( glyphs + index );
259                 Vector2& position = *( glyphPositions + index );
260                 position.x -= ( 0.f > glyphInfo.xBearing ) ? glyphInfo.xBearing : 0.f;
261
262                 // Replace the glyph by the ellipsis glyph.
263                 glyphInfo = ellipsisGlyph;
264
265                 // Change the 'x' and 'y' position of the ellipsis glyph.
266
267                 if( position.x > firstPenX )
268                 {
269                   position.x = firstPenX + removedGlypsWidth - ellipsisGlyphWidth;
270                 }
271
272                 position.x += ellipsisGlyph.xBearing;
273                 position.y = penY - ellipsisGlyph.yBearing;
274
275                 inserted = true;
276               }
277             }
278
279             if( !inserted )
280             {
281               if( index > 0u )
282               {
283                 --index;
284               }
285               else
286               {
287                 // No space for the ellipsis.
288                 inserted = true;
289               }
290               ++numberOfRemovedGlyphs;
291             }
292           }
293
294           // 'Removes' all the glyphs after the ellipsis glyph.
295           numberOfLaidOutGlyphs -= numberOfRemovedGlyphs;
296         }
297       }
298     }
299   }
300
301   return numberOfLaidOutGlyphs;
302 }
303
304 const Vector4& View::GetTextColor() const
305 {
306   if( mImpl->mVisualModel )
307   {
308     return mImpl->mVisualModel->GetTextColor();
309   }
310   return Vector4::ZERO;
311 }
312
313 const Vector2& View::GetShadowOffset() const
314 {
315   if( mImpl->mVisualModel )
316   {
317     return mImpl->mVisualModel->GetShadowOffset();
318   }
319   return Vector2::ZERO;
320 }
321
322 const Vector4& View::GetShadowColor() const
323 {
324   if( mImpl->mVisualModel )
325   {
326     return mImpl->mVisualModel->GetShadowColor();
327   }
328   return Vector4::ZERO;
329 }
330
331 const Vector4& View::GetUnderlineColor() const
332 {
333   if( mImpl->mVisualModel )
334   {
335     return mImpl->mVisualModel->GetUnderlineColor();
336   }
337   return Vector4::ZERO;
338 }
339
340 bool View::IsUnderlineEnabled() const
341 {
342   if( mImpl->mVisualModel )
343   {
344     return mImpl->mVisualModel->IsUnderlineEnabled();
345   }
346   return false;
347 }
348
349 float View::GetUnderlineHeight() const
350 {
351   if( mImpl->mVisualModel )
352   {
353     return mImpl->mVisualModel->GetUnderlineHeight();
354   }
355   return 0.0f;
356 }
357
358 Length View::GetNumberOfUnderlineRuns() const
359 {
360   if( mImpl->mVisualModel )
361   {
362     return mImpl->mVisualModel->mUnderlineRuns.Count();
363   }
364
365   return 0u;
366 }
367
368 void View::GetUnderlineRuns( GlyphRun* underlineRuns,
369                              UnderlineRunIndex index,
370                              Length numberOfRuns ) const
371 {
372   if( mImpl->mVisualModel )
373   {
374     mImpl->mVisualModel->GetUnderlineRuns( underlineRuns,
375                                            index,
376                                            numberOfRuns );
377   }
378 }
379
380 } // namespace Text
381
382 } // namespace Toolkit
383
384 } // namespace Dali