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