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