305abf766cd2435473eeb6311163e21f18faf003
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / 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/internal/text/rendering/basic/text-basic-renderer.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/text-abstraction/font-client.h>
23 #include <dali/public-api/actors/image-actor.h>
24 #include <dali/public-api/actors/mesh-actor.h>
25 #include <dali/public-api/images/atlas.h>
26 #include <dali/public-api/geometry/mesh.h>
27 #include <dali/integration-api/debug.h>
28
29 // INTERNAL INCLUDES
30 #include <dali-toolkit/internal/text/line-run.h>
31 #include <dali-toolkit/internal/text/rendering/shaders/text-basic-shader.h>
32 #include <dali-toolkit/internal/text/rendering/shaders/text-bgra-shader.h>
33
34
35 using namespace Dali;
36 using namespace Dali::Toolkit;
37 using namespace Dali::Toolkit::Text;
38
39 namespace
40 {
41
42 #if defined(DEBUG_ENABLED)
43   Debug::Filter* gLogFilter = Debug::Filter::New(Debug::Concise, true, "LOG_TEXT_RENDERING");
44 #endif
45
46 const std::size_t PADDING = 2; //< To avoid GL filtering artefacts
47
48 struct TextureCoordinates
49 {
50   TextureCoordinates()
51   : topLeft( 0.0f, 0.0f ),
52     topRight( 1.0f, 0.0f ),
53     bottomLeft( 0.0f, 1.0f ),
54     bottomRight( 1.0f, 1.0f )
55   {
56   }
57
58   Vector2 topLeft;
59   Vector2 topRight;
60   Vector2 bottomLeft;
61   Vector2 bottomRight;
62 };
63
64 struct AtlasGlyph
65 {
66   AtlasGlyph()
67   : fontId( 0 ),
68     index( 0 ),
69     xOffset( 0 ),
70     width( 0 ),
71     height( 0 )
72   {
73   }
74
75   AtlasGlyph( FontId id,
76               GlyphIndex glyphIndex,
77               std::size_t offset,
78               std::size_t widthPixels,
79               std::size_t heightPixels,
80               BufferImage bitmap )
81   : fontId( id ),
82     index( glyphIndex ),
83     xOffset( offset ),
84     width( widthPixels ),
85     height( heightPixels ),
86     mBitmap( bitmap )
87   {
88   }
89
90   FontId fontId;
91   GlyphIndex index;
92   std::size_t xOffset;
93   std::size_t width;
94   std::size_t height;
95   BufferImage mBitmap;
96   TextureCoordinates coords;
97 };
98
99 } // unnamed namespace
100
101 struct BasicRenderer::Impl
102 {
103   /**
104    * @brief Create the renderer implementation.
105    */
106   Impl()
107   : mWidthL8( 0.0f ),
108     mHeightL8( 0.0f ),
109     mWidthBGRA8888( 0.0f ),
110     mHeightBGRA8888( 0.0f )
111   {
112     mFontClient = TextAbstraction::FontClient::Get();
113   }
114
115   /**
116    * @brief Reset the previous glyph calculations.
117    *
118    * @param[in] size The glyph space to reserve.
119    */
120   void Reset( std::size_t size )
121   {
122     mWidthL8 = 0.0f;
123     mHeightL8 = 0.0f;
124     mWidthBGRA8888 = 0.0f;
125     mHeightBGRA8888 = 0.0f;
126     mGlyphs.clear();
127     mGlyphs.reserve( size );
128     mAtlasL8.Reset();
129     mAtlasBGRA8888.Reset();
130   }
131
132   /**
133    * @brief Ccreate an Atlas, uploading the necessary glyph bitmaps
134    *
135    * @param[in] glyphs The glyphs to upload.
136    */
137   void CreateAtlases( const Vector<GlyphInfo>& glyphs )
138   {
139     // Clear previous atlas
140     Reset( glyphs.Count() );
141
142     for( unsigned int i=0; i<glyphs.Count(); ++i )
143     {
144       float width  = glyphs[i].width;
145       float height = glyphs[i].height;
146
147       if( width > 0 &&
148           height > 0 ) // skip whitespace
149       {
150         if( !GlyphFound( glyphs[i].fontId, glyphs[i].index ) )
151         {
152           AddGlyph( glyphs[i] );
153         }
154       }
155     }
156
157     mAtlasL8 = CreateAtlas( mWidthL8, mHeightL8, Pixel::L8 );
158     mAtlasBGRA8888 = CreateAtlas( mWidthBGRA8888, mHeightBGRA8888, Pixel::BGRA8888 );
159   }
160
161   Atlas CreateAtlas( unsigned int width, unsigned int height, Pixel::Format format )
162   {
163     Atlas atlas;
164
165     if( width > 0 && height > 0 )
166     {
167       atlas = Atlas::New( width, height, format );
168
169       for( unsigned int i=0; i<mGlyphs.size(); ++i )
170       {
171         AtlasGlyph& glyph = mGlyphs[i];
172
173         const Pixel::Format glyphFormat = glyph.mBitmap.GetPixelFormat();
174
175         if( format == glyphFormat )
176         {
177           atlas.Upload( glyph.mBitmap, glyph.xOffset, 0 );
178
179           TextureCoordinates& coords = glyph.coords;
180           coords.topLeft.x     = static_cast<float>(glyph.xOffset) / static_cast<float>(width);
181           coords.topLeft.y     = 0.0f;
182           coords.topRight.x    = static_cast<float>(glyph.xOffset + glyph.width) / static_cast<float>(width);
183           coords.topRight.y    = 0.0f;
184           coords.bottomLeft.x  = static_cast<float>(glyph.xOffset) / static_cast<float>(width);
185           coords.bottomLeft.y  = static_cast<float>(glyph.height) / static_cast<float>(height);
186           coords.bottomRight.x = static_cast<float>(glyph.xOffset + glyph.width) / static_cast<float>(width);
187           coords.bottomRight.y = static_cast<float>(glyph.height) / static_cast<float>(height);
188         }
189       }
190     }
191
192     return atlas;
193   }
194
195   /**
196    * @brief Check whether we already have the glyph.
197    */
198   bool GlyphFound( FontId fontId, GlyphIndex index ) const
199   {
200     for( unsigned int i=0; i<mGlyphs.size(); ++i )
201     {
202       const AtlasGlyph& glyph = mGlyphs[i];
203
204       if( fontId == glyph.fontId &&
205           index  == glyph.index )
206       {
207         return true;
208       }
209     }
210
211     return false;
212   }
213
214   /**
215    * @brief Add the glyph.
216    */
217   void AddGlyph( const GlyphInfo& glyphInfo )
218   {
219     BufferImage bitmap = mFontClient.CreateBitmap( glyphInfo.fontId, glyphInfo.index );
220
221     const Pixel::Format format = bitmap.GetPixelFormat();
222
223     if( Pixel::L8 == format )
224     {
225       mGlyphs.push_back( AtlasGlyph( glyphInfo.fontId, glyphInfo.index, mWidthL8, glyphInfo.width, glyphInfo.height, bitmap ) );
226
227       // Increase the Atlas width/height
228       mWidthL8 += glyphInfo.width + PADDING;
229       if( mHeightL8 < glyphInfo.height + PADDING )
230       {
231         mHeightL8 = glyphInfo.height + PADDING;
232       }
233     }
234     else if ( Pixel::BGRA8888 == format )
235     {
236        mGlyphs.push_back( AtlasGlyph( glyphInfo.fontId, glyphInfo.index, mWidthBGRA8888, glyphInfo.width, glyphInfo.height, bitmap ) );
237
238       // A separate Atlas is used for color Emojis
239       mWidthBGRA8888 += glyphInfo.width + PADDING;
240       if( mHeightBGRA8888 < glyphInfo.height + PADDING )
241       {
242         mHeightBGRA8888 = glyphInfo.height + PADDING;
243       }
244     }
245   }
246
247   /**
248    * @brief Get the texture coordinates for a glyph.
249    */
250   bool GetTextureCoordinates( Pixel::Format format, FontId fontId, GlyphIndex index, TextureCoordinates& coords )
251   {
252     for( unsigned int i=0; i<mGlyphs.size(); ++i )
253     {
254       const AtlasGlyph& glyph = mGlyphs[i];
255
256       const Pixel::Format glyphFormat = glyph.mBitmap.GetPixelFormat();
257
258       if( format == glyphFormat &&
259           fontId == glyph.fontId &&
260           index  == glyph.index )
261       {
262         coords = glyph.coords;
263         return true;
264       }
265     }
266
267     return false;
268   }
269
270   /**
271    * @brief Helper method to create a mesh with one quad per glyph.
272    *
273    * @param[in] glyphs The glyphs to display.
274    * @param[in] positions The 2D positions of the glyphs.
275    * @param[in] image The material uses this as a diffuse texture.
276    */
277   Mesh CreateMesh( const Vector<GlyphInfo>& glyphs, const std::vector<Vector2>& positions, Pixel::Format format, Image image )
278   {
279     MeshData::VertexContainer vertices( 4 * glyphs.Count() ); // 1 quad per glyph
280
281     MeshData::FaceIndices faces;
282     faces.reserve( 6 * glyphs.Count() ); // 2 triangles per quad
283
284     for( unsigned int i=0; i<glyphs.Count(); ++i )
285     {
286       float width  = glyphs[i].width;
287       float height = glyphs[i].height;
288
289       if( width > 0 &&
290           height > 0 ) // skip whitespace
291       {
292         const Vector2& position = positions[i];
293
294         TextureCoordinates coords;
295         if( GetTextureCoordinates( format, glyphs[i].fontId, glyphs[i].index, coords ) )
296         {
297           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 ) );
298           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 ) );
299           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 ) );
300           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 ) );
301
302           faces.push_back( i*4 + 0 ); faces.push_back( i*4 + 3 ); faces.push_back( i*4 + 1 );
303           faces.push_back( i*4 + 0 ); faces.push_back( i*4 + 2 ); faces.push_back( i*4 + 3 );
304         }
305       }
306     }
307
308     Material material = Material::New( "Material" );
309     material.SetDiffuseTexture( image );
310
311     // Create the mesh data from the vertices and faces
312     MeshData meshData;
313     meshData.SetHasColor( false );
314     meshData.SetMaterial( material );
315     meshData.SetVertices( vertices );
316     meshData.SetFaceIndices( faces );
317
318     // Create a mesh from the data
319     Dali::Mesh mesh = Mesh::New( meshData );
320     return mesh;
321   }
322
323   RenderableActor mActor; ///< The actor which renders the text
324
325   Atlas mAtlasL8;
326   unsigned int mWidthL8;
327   unsigned int mHeightL8;
328
329   // A separate Atlas is used for color Emojis
330   Atlas mAtlasBGRA8888;
331   unsigned int mWidthBGRA8888;
332   unsigned int mHeightBGRA8888;
333
334   std::vector<AtlasGlyph> mGlyphs;
335
336   TextAbstraction::FontClient mFontClient;
337 };
338
339 Text::RendererPtr BasicRenderer::New()
340 {
341   DALI_LOG_INFO( gLogFilter, Debug::Verbose, "Text::BasicRenderer::New()\n" );
342
343   return Text::RendererPtr( new BasicRenderer() );
344 }
345
346 RenderableActor BasicRenderer::Render( Text::ViewInterface& view )
347 {
348   // Remove the previous text
349   UnparentAndReset( mImpl->mActor );
350
351   Length numberOfGlyphs = view.GetNumberOfGlyphs();
352
353   if( numberOfGlyphs > 0u )
354   {
355     Vector<GlyphInfo> glyphs;
356     glyphs.Resize( numberOfGlyphs );
357
358     std::vector<Vector2> positions;
359     positions.resize( numberOfGlyphs );
360
361     numberOfGlyphs = view.GetGlyphs( glyphs.Begin(),
362                                      &positions[0],
363                                      0u,
364                                      numberOfGlyphs );
365
366     glyphs.Resize( numberOfGlyphs );
367     positions.resize( numberOfGlyphs );
368
369     mImpl->CreateAtlases( glyphs );
370
371     MeshActor actorL8;
372     if( mImpl->mAtlasL8 )
373     {
374       actorL8 = MeshActor::New( mImpl->CreateMesh( glyphs, positions, Pixel::L8, mImpl->mAtlasL8 ) );
375       actorL8.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
376
377       ShaderEffect shader = BasicShader::New();
378       actorL8.SetShaderEffect( shader );
379     }
380
381     MeshActor actorBGRA8888;
382     if( mImpl->mAtlasBGRA8888 )
383     {
384       actorBGRA8888 = MeshActor::New( mImpl->CreateMesh( glyphs, positions, Pixel::BGRA8888, mImpl->mAtlasBGRA8888 ) );
385       actorBGRA8888.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
386
387       ShaderEffect shader = BgraShader::New();
388       actorBGRA8888.SetShaderEffect( shader );
389     }
390
391     // If we have both monochrome & color glyphs, two mesh actors are returned in a container
392     if( actorL8 && actorBGRA8888 )
393     {
394       mImpl->mActor = ImageActor::New();
395       mImpl->mActor.Add( actorL8 );
396       mImpl->mActor.Add( actorBGRA8888 );
397     }
398     else
399     {
400       if( actorL8 )
401       {
402         mImpl->mActor = actorL8;
403       }
404       else if( actorBGRA8888 )
405       {
406         mImpl->mActor = actorBGRA8888;
407       }
408     }
409   }
410
411   return mImpl->mActor;
412 }
413
414 BasicRenderer::BasicRenderer()
415 {
416   mImpl = new Impl();
417 }
418
419 BasicRenderer::~BasicRenderer()
420 {
421   delete mImpl;
422 }