TextLabel::GetHeightForWidth() implementation.
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / public-api / text / rendering / basic / text-basic-renderer.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/public-api/text/rendering/basic/text-basic-renderer.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/text-abstraction/font-client.h>
23 #include <dali/public-api/actors/mesh-actor.h>
24 #include <dali/public-api/images/atlas.h>
25 #include <dali/public-api/geometry/mesh.h>
26 #include <dali-toolkit/public-api/text/rendering/shaders/text-basic-shader.h>
27
28 using namespace Dali;
29 using namespace Dali::Toolkit;
30 using namespace Dali::Toolkit::Text;
31
32 namespace
33 {
34
35 const std::size_t PADDING = 2; //< To avoid GL filtering artefacts
36
37 struct TextureCoordinates
38 {
39   TextureCoordinates()
40   : topLeft( 0.0f, 0.0f ),
41     topRight( 1.0f, 0.0f ),
42     bottomLeft( 0.0f, 1.0f ),
43     bottomRight( 1.0f, 1.0f )
44   {
45   }
46
47   Vector2 topLeft;
48   Vector2 topRight;
49   Vector2 bottomLeft;
50   Vector2 bottomRight;
51 };
52
53 struct AtlasHelperGlyph
54 {
55   AtlasHelperGlyph()
56   : fontId( 0 ),
57     index( 0 ),
58     xOffset( 0 ),
59     width( 0 ),
60     height( 0 )
61   {
62   }
63
64   AtlasHelperGlyph( FontId id,
65                     GlyphIndex glyphIndex,
66                     std::size_t offset,
67                     std::size_t widthPixels,
68                     std::size_t heightPixels )
69   : fontId( id ),
70     index( glyphIndex ),
71     xOffset( offset ),
72     width( widthPixels ),
73     height( heightPixels )
74   {
75   }
76
77   FontId fontId;
78   GlyphIndex index;
79   std::size_t xOffset;
80   std::size_t width;
81   std::size_t height;
82   TextureCoordinates coords;
83 };
84
85 struct AtlasHelper
86 {
87   AtlasHelper()
88   : mWidth( 0.0f ),
89     mHeight( 0.0f )
90   {
91     mFontClient = TextAbstraction::FontClient::Get();
92   }
93
94   void Reset()
95   {
96     mWidth = 0.0f;
97     mHeight = 0.0f;
98     mGlyphs.clear();
99   }
100
101   void Reserve( std::size_t size )
102   {
103     mGlyphs.reserve( size );
104   }
105
106   bool GlyphFound( FontId fontId, GlyphIndex index ) const
107   {
108     for( unsigned int i=0; i<mGlyphs.size(); ++i )
109     {
110       const AtlasHelperGlyph& glyph = mGlyphs[i];
111
112       if( fontId == glyph.fontId &&
113           index  == glyph.index )
114       {
115         return true;
116       }
117     }
118
119     return false;
120   }
121
122   void AddGlyph( const GlyphInfo& glyphInfo )
123   {
124     mGlyphs.push_back( AtlasHelperGlyph( glyphInfo.fontId, glyphInfo.index, mWidth, glyphInfo.width, glyphInfo.height ) );
125
126     mWidth += glyphInfo.width + PADDING;
127     if( mHeight < glyphInfo.height + PADDING )
128     {
129       mHeight = glyphInfo.height + PADDING;
130     }
131   }
132
133   Atlas CreateAtlas()
134   {
135     Atlas atlas = Atlas::New( mWidth, mHeight, Pixel::L8 );
136
137     for( unsigned int i=0; i<mGlyphs.size(); ++i )
138     {
139       AtlasHelperGlyph& glyph = mGlyphs[i];
140       BitmapImage bitmap = mFontClient.CreateBitmap( glyph.fontId, glyph.index );
141       atlas.Upload( bitmap, glyph.xOffset, 0 );
142
143       TextureCoordinates& coords = glyph.coords;
144       coords.topLeft.x     = static_cast<float>(glyph.xOffset) / static_cast<float>(mWidth);
145       coords.topLeft.y     = 0.0f;
146       coords.topRight.x    = static_cast<float>(glyph.xOffset + glyph.width) / static_cast<float>(mWidth);
147       coords.topRight.y    = 0.0f;
148       coords.bottomLeft.x  = static_cast<float>(glyph.xOffset) / static_cast<float>(mWidth);
149       coords.bottomLeft.y  = static_cast<float>(glyph.height) / static_cast<float>(mHeight);
150       coords.bottomRight.x = static_cast<float>(glyph.xOffset + glyph.width) / static_cast<float>(mWidth);
151       coords.bottomRight.y = static_cast<float>(glyph.height) / static_cast<float>(mHeight);
152     }
153
154     return atlas;
155   }
156
157   void GetTextureCoordinates( FontId fontId, GlyphIndex index, TextureCoordinates& coords )
158   {
159     for( unsigned int i=0; i<mGlyphs.size(); ++i )
160     {
161       const AtlasHelperGlyph& glyph = mGlyphs[i];
162
163       if( fontId == glyph.fontId &&
164           index  == glyph.index )
165       {
166         coords = glyph.coords;
167         return;
168       }
169     }
170   }
171
172 private: // Data
173
174   std::size_t mWidth;
175   std::size_t mHeight;
176
177   std::vector<AtlasHelperGlyph> mGlyphs;
178
179   TextAbstraction::FontClient mFontClient;
180 };
181
182 } // unnamed namespace
183
184 struct BasicRenderer::Impl
185 {
186   /**
187    * @brief Ccreate an Atlas, uploading the necessary glyph bitmaps
188    *
189    * @param[in] glyphs The glyphs to upload.
190    */
191   Atlas CreateAtlas( const Vector<GlyphInfo>& glyphs )
192   {
193     AtlasHelper& helper = mAtlasHelper;
194
195     // Clear previous atlas
196     helper.Reset();
197     helper.Reserve( glyphs.Count() );
198
199     for( unsigned int i=0; i<glyphs.Count(); ++i )
200     {
201       float width  = glyphs[i].width;
202       float height = glyphs[i].height;
203
204       if( width > 0 &&
205           height > 0 ) // skip whitespace
206       {
207         if( !helper.GlyphFound( glyphs[i].fontId, glyphs[i].index ) )
208         {
209           helper.AddGlyph( glyphs[i] );
210         }
211       }
212     }
213
214     // Uploads the bitmaps to Dali
215     return helper.CreateAtlas();
216   }
217
218   /**
219    * @brief Helper method to create a mesh with one quad per glyph.
220    *
221    * @param[in] glyphs The glyphs to display.
222    * @param[in] positions The 2D positions of the glyphs.
223    * @param[in] image The material uses this as a diffuse texture.
224    */
225   Mesh CreateMesh( const Vector<GlyphInfo>& glyphs, const std::vector<Vector2>& positions, Image image )
226   {
227     MeshData::VertexContainer vertices( 4 * glyphs.Count() ); // 1 quad per glyph
228
229     MeshData::FaceIndices faces;
230     faces.reserve( 6 * glyphs.Count() ); // 2 triangles per quad
231
232     for( unsigned int i=0; i<glyphs.Count(); ++i )
233     {
234       float width  = glyphs[i].width;
235       float height = glyphs[i].height;
236
237       if( width > 0 &&
238           height > 0 ) // skip whitespace
239       {
240         const Vector2& position = positions[i];
241
242         TextureCoordinates coords;
243         mAtlasHelper.GetTextureCoordinates( glyphs[i].fontId, glyphs[i].index, coords );
244
245         vertices[ i*4 + 0 ] = MeshData::Vertex( Vector3( position.x + 0.0f*width, position.y + 0.0f*height, 0.0f ), coords.topLeft,     Vector3( 1.0f, 0.0f, 0.0f ) );
246         vertices[ i*4 + 1 ] = MeshData::Vertex( Vector3( position.x + 1.0f*width, position.y + 0.0f*height, 0.0f ), coords.topRight,    Vector3( 1.0f, 1.0f, 0.0f ) );
247         vertices[ i*4 + 2 ] = MeshData::Vertex( Vector3( position.x + 0.0f*width, position.y + 1.0f*height, 0.0f ), coords.bottomLeft,  Vector3( 0.0f, 1.0f, 0.0f ) );
248         vertices[ i*4 + 3 ] = MeshData::Vertex( Vector3( position.x + 1.0f*width, position.y + 1.0f*height, 0.0f ), coords.bottomRight, Vector3( 0.0f, 0.0f, 1.0f ) );
249
250         faces.push_back( i*4 + 0 ); faces.push_back( i*4 + 3 ); faces.push_back( i*4 + 1 );
251         faces.push_back( i*4 + 0 ); faces.push_back( i*4 + 2 ); faces.push_back( i*4 + 3 );
252       }
253     }
254
255     Material material = Material::New( "Material" );
256     material.SetDiffuseTexture( image );
257
258     // Create the mesh data from the vertices and faces
259     MeshData meshData;
260     meshData.SetHasColor( false );
261     meshData.SetMaterial( material );
262     meshData.SetVertices( vertices );
263     meshData.SetFaceIndices( faces );
264
265     // Create a mesh from the data
266     Dali::Mesh mesh = Mesh::New( meshData );
267     return mesh;
268   }
269
270   RenderableActor mActor; ///< The actor which renders the text
271
272   AtlasHelper mAtlasHelper; ///< A helper class for storing atlas positions etc.
273 };
274
275 Text::RendererPtr BasicRenderer::New()
276 {
277   return Text::RendererPtr( new BasicRenderer() );
278 }
279
280 RenderableActor BasicRenderer::Render( Text::ViewInterface& view )
281 {
282   Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
283
284   if( numberOfGlyphs > 0 )
285   {
286     Vector<GlyphInfo> glyphs;
287     glyphs.Resize( numberOfGlyphs );
288
289     view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
290
291     std::vector<Vector2> positions;
292     positions.resize( numberOfGlyphs );
293     view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
294
295     Atlas atlas = mImpl->CreateAtlas( glyphs );
296
297     MeshActor actor = MeshActor::New( mImpl->CreateMesh( glyphs, positions, atlas ) );
298     actor.SetParentOrigin( ParentOrigin::TOP_LEFT );
299     actor.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
300     actor.SetAffectedByLighting( false );
301
302     ShaderEffect shader = BasicShader::New();
303     actor.SetShaderEffect( shader );
304
305     mImpl->mActor = actor;
306   }
307
308   return mImpl->mActor;
309 }
310
311 BasicRenderer::BasicRenderer()
312 {
313   mImpl = new Impl();
314 }
315
316 BasicRenderer::~BasicRenderer()
317 {
318   delete mImpl;
319 }