License conversion from Flora to Apache 2.0
[platform/core/uifw/dali-core.git] / dali / internal / event / text / generator / text-vertex-generator.cpp
1 /*
2  * Copyright (c) 2014 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 "text-vertex-generator.h"
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/common/constants.h>
23 #include <dali/internal/event/text/glyph-status/glyph-status.h>
24 #include <dali/internal/event/text/special-characters.h>
25
26 // EXTERNAL INCLUDES
27 #include <cmath>  // for std::sin
28
29 namespace Dali
30 {
31
32 namespace Internal
33 {
34
35 namespace // unnamed namespace
36 {
37
38 typedef std::vector<TextVertex2D> VertexBuffer;
39
40 void RepositionData( VertexBuffer& buffer, Vector2 offset )
41 {
42   /*
43    *
44    * As 0,0 is the middle of the actor, text will be displayed like this
45    *
46    *  |-------------------------------|
47    *  |            Actor              |
48    *  |                               |
49    *  |                               |
50    *  |          (0,0)|----------     | (x)
51    *  |               | Hello World   |
52    *  |               |               |
53    *  |               |               |
54    *  |-------------------------------|
55    *                 (y)
56    *
57    *  Below it is repositioned to the centre of the actor
58    *  |-------------------------------|
59    *  |            Actor              |
60    *  |                               |
61    *  |                               |
62    *  |          Hello World------    | (x)
63    *  |               |               |
64    *  |               |               |
65    *  |               |               |
66    *  |-------------------------------|
67    */
68
69   // move the vertices so 0,0 is the centre of the text string.
70   offset.x/=2.0f;
71   offset.y/=2.0f;
72
73   for (std::size_t i=0, size = buffer.size() ; i< size; ++i)
74   {
75       buffer[i].mX -= offset.x;
76       buffer[i].mY -= offset.y;
77   }
78 }
79
80 void AddVertex( VertexBuffer& vertexBuffer,
81                 const float xPos,
82                 const float yPos,
83                 const float charWidth,
84                 const float charHeight,
85                 const UvRect& uv,
86                 const Vector2& uvShadow )
87 {
88   /*
89    * Create 4 vertices
90    * 1 --- 2
91    * |    /|
92    * |  A  |
93    * | /   |
94    * 0 --- 3
95    *
96    * 2 triangles with clock wise winding: 0->1->2 and 0->2->3
97    */
98
99   TextVertex2D v;
100
101   // set U1,V1 for all vertices
102   v.mU1 = uvShadow.x;
103   v.mV1 = uvShadow.y;
104
105   // bottom left, 0
106   v.mX = xPos;
107   v.mY = yPos;
108   v.mU = uv.u0;
109   v.mV = uv.v0;
110   vertexBuffer.push_back(v);
111
112   // top left, 1
113   v.mX = xPos;
114   v.mY = yPos + charHeight;
115   v.mU = uv.u0;
116   v.mV = uv.v2;
117   vertexBuffer.push_back(v);
118
119   // top right, 2
120   v.mX = xPos + charWidth;
121   v.mY = yPos + charHeight;
122   v.mU = uv.u2;
123   v.mV = uv.v2;
124   vertexBuffer.push_back(v);
125
126   // bottom right, 3
127   v.mX = xPos + charWidth;
128   v.mY = yPos;
129   v.mU = uv.u2;
130   v.mV = uv.v0;
131   vertexBuffer.push_back(v);
132 }
133
134 /**
135  * Adjust the vertex data for italics.
136  * Skews the vertices by a value
137  */
138 void AdjustForItalics( VertexBuffer&  vertexBuffer,
139                        const float italicsTopDisplacement,
140                        const float italicsBottomDisplacement)
141 {
142
143   std::size_t index = vertexBuffer.size()-4;
144   TextVertex2D &v1 = vertexBuffer.at( index );
145   v1.mX+= italicsBottomDisplacement;
146
147
148   // top left
149   index++;
150   TextVertex2D &v2 = vertexBuffer.at( index );
151   v2.mX+= italicsTopDisplacement;
152
153
154   // top right
155   index++;
156   TextVertex2D &v3 = vertexBuffer.at( index );
157   v3.mX+= italicsTopDisplacement;
158
159   // bottom right
160   index++;
161   TextVertex2D &v4 = vertexBuffer.at( index );
162   v4.mX+= italicsBottomDisplacement;
163 }
164
165 void AddUnderline( VertexBuffer& vertexBuffer,
166                    const float totalWidth,
167                    const float thickness,
168                    const float yPosition,
169                    const UvRect& uv )
170 {
171   /*
172    *  Add an underline to a string of text.
173    *
174    *
175    *  A thin vertical slice of the underline character is stretched to the
176    *  length of the string.
177    *
178    *  If we stretch the entire underline character (not a thin slice) then
179    *  the rounded edges will be stretched as well, giving inconsistent results.
180    *
181    *   Underline glyph                     Only use a thin slice for texturing
182    *
183    *  |-------------------|           (u0,v2)|--------|X|---------|(u2,v2)
184    *  |                   |                  |        |X|         |
185    *  |   /-----------\   |                  |   /----|X|-----\   |
186    *  |  |  underline  |  |                  |  |     |X|     |   |
187    *  |   \___________/   |                  |   \____|X|_____/   |
188    *  |                   |                  |        |X|         |
189    *  |                   |                  |        |X|         |
190    *  |-------------------|                  |--------|X|---------|
191    *
192    *                                      (u0,v0)    halfU       (u2,v0)
193    *
194    *  In calculation below
195    *  HalfU = half way between u0 and u2. This gives a thin slice.
196    *  So we use the texture-coordinates from  (halfU, v0) -> (halfU, v2).
197    *
198    *  End result is: A solid edge on the left / right side of the underline:
199    *                 A smooth (anti-aliased) edge on the top / bottom of the underline
200    */
201
202   TextVertex2D v;
203
204   // set U1,V1 for all vertices
205   v.mU1 = 1.0f;
206   v.mV1 = 1.0f;
207
208   float halfU = (uv.u0 + uv.u2)/2.0f;
209
210   /*
211    * Create 4 vertices
212    * 1 --- 2
213    * |    /|
214    * |  A  |
215    * | /   |
216    * 0 --- 3
217    */
218
219   // 0
220   v.mX = 0.0f;
221   v.mY = yPosition;
222   v.mU = halfU;
223   v.mV = uv.v2;
224   vertexBuffer.push_back(v);
225
226   // 1
227   v.mX = 0.0f;
228   v.mY = yPosition + thickness;
229   v.mU = halfU;
230   v.mV = uv.v0;
231   vertexBuffer.push_back(v);
232
233   // 2
234   v.mX = totalWidth;
235   v.mY = yPosition + thickness;
236   v.mU = halfU;
237   v.mV = uv.v0;
238   vertexBuffer.push_back(v);
239
240   // 3
241   v.mX = totalWidth;
242   v.mY = yPosition;
243   v.mU = halfU;
244   v.mV = uv.v2;
245   vertexBuffer.push_back(v);
246 }
247
248 void GetAdjustedSize(float &charWidth,
249                      float &charHeight,
250                      float &left,
251                      float &top,
252                      float padAdjustX,
253                      float padAdjustY,
254                      float scalar,
255                      const GlyphMetric& glyph)
256 {
257   charWidth  = (glyph.GetWidth()  + padAdjustX * 2.0f) * scalar;
258   charHeight = (glyph.GetHeight() + padAdjustY * 2.0f) * scalar;
259   left = (glyph.GetLeft() - padAdjustX) * scalar;
260   top  = (glyph.GetTop()  + padAdjustY) * scalar;
261 }
262
263 #ifdef DEBUG_VERTS
264
265 void DebugVertexBuffer( VertexBuffer& buffer )
266 {
267   for (std::size_t i = 0, size = buffer.size(); i< size ; ++i)
268   {
269     TextVertex2D &v = buffer.at( i );
270     printf("%d: xyuv =, %f , %f, %f, %f  \n", (unsigned int) i, v.mX,v.mY, v.mU, v.mV);
271   }
272 }
273 #endif
274
275 } // unnamed namespace
276
277 TextVertexBuffer* TextVertexGenerator::Generate(const TextArray& text,
278                            const TextFormat& format,
279                            const FontMetricsInterface& metrics,
280                            const AtlasUvInterface& uvInterface,
281                            const FontId fontId)
282
283 {
284   TextVertexBuffer* textVertexBuffer = new TextVertexBuffer;
285   VertexBuffer &vertexBuffer(textVertexBuffer->mVertices);
286
287   const GlyphMetric* glyph( NULL );
288   float xPos( 0.0f );
289   float yPos( 0.0f );
290   float underlineWidth( 0.0f );
291   float totalWidth( 0.0f );
292   float charWidth( 0.0f );
293   float charHeight( 0.0f );
294   float left(0.0f);
295   float top(0.0f);
296
297   float scalar = metrics.GetUnitsToPixels( format.GetPointSize() );
298
299   // Italics displacement
300   // the text is rendered upside down
301   const float sinAngle = format.IsItalic() ? std::sin( format.GetItalicsAngle() ) : 0.0f;
302
303   // get the line height and ascender from the font
304   const float lineHeight( metrics.GetLineHeight() * scalar );
305   const float ascender( metrics.GetAscender() * scalar );
306   const float padAdjustX( metrics.GetPadAdjustX() );
307   const float padAdjustY( metrics.GetPadAdjustY() );
308   const float tileWidth( metrics.GetMaxWidth() * scalar );
309   const float tileHeight( metrics.GetMaxHeight() * scalar );
310   unsigned int textSize = text.size();
311
312   for (unsigned int i = 0; i < textSize; ++i)
313   {
314     // buffer is always filled starting from the first vector position. However text characters are visited from left to right or from right to left.
315     uint32_t charIndex = text[ ( format.IsLeftToRight() ? i : ( textSize - 1 - i ) ) ];
316
317     glyph = metrics.GetGlyph( charIndex );
318
319     if (charIndex >= SpecialCharacters::FIRST_VISIBLE_CHAR && glyph )
320     {
321       // get char size and offset adjusted for padding in the atlas
322       GetAdjustedSize(charWidth, charHeight, left, top, padAdjustX, padAdjustY, scalar, *glyph );
323
324       yPos = (ascender - top);
325       xPos += left;
326
327       // a combination of character index and font id is used to uniquely identify the character
328       unsigned int encodedChar = GlyphStatus::GetEncodedValue( charIndex, fontId );
329       UvRect uv = uvInterface.GetUvCoordinates( encodedChar );
330
331       const Vector2 uvShadow( tileWidth / charWidth, tileHeight / charHeight );
332
333       AddVertex( vertexBuffer, xPos, yPos, charWidth, charHeight, uv, uvShadow );
334
335       if( format.IsItalic() )
336       {
337         float italicsTopDisplacement = ( top - charHeight ) * sinAngle;
338         float italicsBottomDisplacement = top * sinAngle;
339         AdjustForItalics( vertexBuffer, italicsTopDisplacement, italicsBottomDisplacement);
340       }
341
342       xPos -= left;
343     }
344
345     if( glyph )
346     {
347       underlineWidth = std::max( underlineWidth, xPos + glyph->GetXAdvance() * scalar );
348       xPos += glyph->GetXAdvance() * scalar;
349       totalWidth = std::max(totalWidth, xPos);
350     }
351   } // for
352
353   if( format.IsUnderLined() )
354   {
355     unsigned int encodedChar = GlyphStatus::GetEncodedValue( SpecialCharacters::UNDERLINE_CHARACTER, fontId );
356     UvRect uv( uvInterface.GetUvCoordinates( encodedChar ));
357
358     glyph = metrics.GetGlyph( SpecialCharacters::UNDERLINE_CHARACTER );
359
360     if( glyph )
361     {
362       // Adjust uv coordinates for scaling within atlas tile
363       GetAdjustedSize(charWidth, charHeight, left, top, padAdjustX, padAdjustY, scalar, *glyph );
364
365       // Get underline thickness and position.
366       // These values could be retrieved from the text-format, set to the text-actor through text-style,
367       // or retrieved directly from the font metrics.
368       float thickness = 0.f;
369       float position = 0.f;
370
371       if( fabs( format.GetUnderlineThickness() ) > Math::MACHINE_EPSILON_0 )
372       {
373         // Thickness and position retrieved from the format, which are passed to the
374         // text-actor through the text-style, it adds the vertical pad adjust used to fit some effects like glow or shadow..
375         thickness = -format.GetUnderlineThickness();
376         position = format.GetUnderlinePosition();
377       }
378       else
379       {
380         // Thickness and position retrieved from the font metrics.
381         // It adds the vertical pad adjust ( padAdjustY ) used to fit some effects like glow or shadow.
382         thickness = -( metrics.GetUnderlineThickness() + 2.f * padAdjustY ) * scalar;
383         position = ascender - ( metrics.GetUnderlinePosition() - padAdjustY ) * scalar;
384       }
385       AddUnderline( vertexBuffer, underlineWidth, thickness, position, uv );
386     }
387   }
388
389   textVertexBuffer->mVertexMax = Vector2(totalWidth,lineHeight);
390
391   RepositionData( vertexBuffer, textVertexBuffer->mVertexMax );
392
393 #ifdef DEBUG_VERTS
394   DebugVertexBuffer( vertexBuffer );
395 #endif
396
397   return textVertexBuffer;
398 }
399
400 } // namespace Internal
401
402 } // namespace Dali