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