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