2 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali-toolkit/internal/text/rendering/basic/text-basic-renderer.h>
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>
30 using namespace Dali::Toolkit;
31 using namespace Dali::Toolkit::Text;
36 const std::size_t PADDING = 2; //< To avoid GL filtering artefacts
38 struct TextureCoordinates
41 : topLeft( 0.0f, 0.0f ),
42 topRight( 1.0f, 0.0f ),
43 bottomLeft( 0.0f, 1.0f ),
44 bottomRight( 1.0f, 1.0f )
65 AtlasGlyph( FontId id,
66 GlyphIndex glyphIndex,
68 std::size_t widthPixels,
69 std::size_t heightPixels,
75 height( heightPixels ),
86 TextureCoordinates coords;
89 } // unnamed namespace
91 struct BasicRenderer::Impl
94 * @brief Create the renderer implementation.
99 mWidthBGRA8888( 0.0f ),
100 mHeightBGRA8888( 0.0f )
102 mFontClient = TextAbstraction::FontClient::Get();
106 * @brief Reset the previous glyph calculations.
108 * @param[in] size The glyph space to reserve.
110 void Reset( std::size_t size )
114 mWidthBGRA8888 = 0.0f;
115 mHeightBGRA8888 = 0.0f;
117 mGlyphs.reserve( size );
119 mAtlasBGRA8888.Reset();
123 * @brief Ccreate an Atlas, uploading the necessary glyph bitmaps
125 * @param[in] glyphs The glyphs to upload.
127 void CreateAtlases( const Vector<GlyphInfo>& glyphs )
129 // Clear previous atlas
130 Reset( glyphs.Count() );
132 for( unsigned int i=0; i<glyphs.Count(); ++i )
134 float width = glyphs[i].width;
135 float height = glyphs[i].height;
138 height > 0 ) // skip whitespace
140 if( !GlyphFound( glyphs[i].fontId, glyphs[i].index ) )
142 AddGlyph( glyphs[i] );
147 mAtlasL8 = CreateAtlas( mWidthL8, mHeightL8, Pixel::L8 );
148 mAtlasBGRA8888 = CreateAtlas( mWidthBGRA8888, mHeightBGRA8888, Pixel::BGRA8888 );
151 Atlas CreateAtlas( unsigned int width, unsigned int height, Pixel::Format format )
155 if( width > 0 && height > 0 )
157 atlas = Atlas::New( width, height, format );
159 for( unsigned int i=0; i<mGlyphs.size(); ++i )
161 AtlasGlyph& glyph = mGlyphs[i];
163 const Pixel::Format glyphFormat = glyph.mBitmap.GetPixelFormat();
165 if( format == glyphFormat )
167 atlas.Upload( glyph.mBitmap, glyph.xOffset, 0 );
169 TextureCoordinates& coords = glyph.coords;
170 coords.topLeft.x = static_cast<float>(glyph.xOffset) / static_cast<float>(width);
171 coords.topLeft.y = 0.0f;
172 coords.topRight.x = static_cast<float>(glyph.xOffset + glyph.width) / static_cast<float>(width);
173 coords.topRight.y = 0.0f;
174 coords.bottomLeft.x = static_cast<float>(glyph.xOffset) / static_cast<float>(width);
175 coords.bottomLeft.y = static_cast<float>(glyph.height) / static_cast<float>(height);
176 coords.bottomRight.x = static_cast<float>(glyph.xOffset + glyph.width) / static_cast<float>(width);
177 coords.bottomRight.y = static_cast<float>(glyph.height) / static_cast<float>(height);
186 * @brief Check whether we already have the glyph.
188 bool GlyphFound( FontId fontId, GlyphIndex index ) const
190 for( unsigned int i=0; i<mGlyphs.size(); ++i )
192 const AtlasGlyph& glyph = mGlyphs[i];
194 if( fontId == glyph.fontId &&
195 index == glyph.index )
205 * @brief Add the glyph.
207 void AddGlyph( const GlyphInfo& glyphInfo )
209 BufferImage bitmap = mFontClient.CreateBitmap( glyphInfo.fontId, glyphInfo.index );
211 const Pixel::Format format = bitmap.GetPixelFormat();
213 if( Pixel::L8 == format )
215 mGlyphs.push_back( AtlasGlyph( glyphInfo.fontId, glyphInfo.index, mWidthL8, glyphInfo.width, glyphInfo.height, bitmap ) );
217 // Increase the Atlas width/height
218 mWidthL8 += glyphInfo.width + PADDING;
219 if( mHeightL8 < glyphInfo.height + PADDING )
221 mHeightL8 = glyphInfo.height + PADDING;
224 else if ( Pixel::BGRA8888 == format )
226 mGlyphs.push_back( AtlasGlyph( glyphInfo.fontId, glyphInfo.index, mWidthBGRA8888, glyphInfo.width, glyphInfo.height, bitmap ) );
228 // A separate Atlas is used for color Emojis
229 mWidthBGRA8888 += glyphInfo.width + PADDING;
230 if( mHeightBGRA8888 < glyphInfo.height + PADDING )
232 mHeightBGRA8888 = glyphInfo.height + PADDING;
238 * @brief Get the texture coordinates for a glyph.
240 bool GetTextureCoordinates( Pixel::Format format, FontId fontId, GlyphIndex index, TextureCoordinates& coords )
242 for( unsigned int i=0; i<mGlyphs.size(); ++i )
244 const AtlasGlyph& glyph = mGlyphs[i];
246 const Pixel::Format glyphFormat = glyph.mBitmap.GetPixelFormat();
248 if( format == glyphFormat &&
249 fontId == glyph.fontId &&
250 index == glyph.index )
252 coords = glyph.coords;
261 * @brief Helper method to create a mesh with one quad per glyph.
263 * @param[in] glyphs The glyphs to display.
264 * @param[in] positions The 2D positions of the glyphs.
265 * @param[in] image The material uses this as a diffuse texture.
267 Mesh CreateMesh( const Vector<GlyphInfo>& glyphs, const std::vector<Vector2>& positions, Pixel::Format format, Image image )
269 MeshData::VertexContainer vertices( 4 * glyphs.Count() ); // 1 quad per glyph
271 MeshData::FaceIndices faces;
272 faces.reserve( 6 * glyphs.Count() ); // 2 triangles per quad
274 for( unsigned int i=0; i<glyphs.Count(); ++i )
276 float width = glyphs[i].width;
277 float height = glyphs[i].height;
280 height > 0 ) // skip whitespace
282 const Vector2& position = positions[i];
284 TextureCoordinates coords;
285 if( GetTextureCoordinates( format, glyphs[i].fontId, glyphs[i].index, coords ) )
287 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 ) );
288 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 ) );
289 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 ) );
290 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 ) );
292 faces.push_back( i*4 + 0 ); faces.push_back( i*4 + 3 ); faces.push_back( i*4 + 1 );
293 faces.push_back( i*4 + 0 ); faces.push_back( i*4 + 2 ); faces.push_back( i*4 + 3 );
298 Material material = Material::New( "Material" );
299 material.SetDiffuseTexture( image );
301 // Create the mesh data from the vertices and faces
303 meshData.SetHasColor( false );
304 meshData.SetMaterial( material );
305 meshData.SetVertices( vertices );
306 meshData.SetFaceIndices( faces );
308 // Create a mesh from the data
309 Dali::Mesh mesh = Mesh::New( meshData );
313 RenderableActor mActor; ///< The actor which renders the text
316 unsigned int mWidthL8;
317 unsigned int mHeightL8;
319 // A separate Atlas is used for color Emojis
320 Atlas mAtlasBGRA8888;
321 unsigned int mWidthBGRA8888;
322 unsigned int mHeightBGRA8888;
324 std::vector<AtlasGlyph> mGlyphs;
326 TextAbstraction::FontClient mFontClient;
329 Text::RendererPtr BasicRenderer::New()
331 return Text::RendererPtr( new BasicRenderer() );
334 RenderableActor BasicRenderer::Render( Text::ViewInterface& view )
336 // Remove the previous text
337 UnparentAndReset( mImpl->mActor );
339 Text::Length numberOfGlyphs = view.GetNumberOfGlyphs();
341 if( numberOfGlyphs > 0 )
343 Vector<GlyphInfo> glyphs;
344 glyphs.Resize( numberOfGlyphs );
346 view.GetGlyphs( &glyphs[0], 0, numberOfGlyphs );
348 std::vector<Vector2> positions;
349 positions.resize( numberOfGlyphs );
350 view.GetGlyphPositions( &positions[0], 0, numberOfGlyphs );
352 mImpl->CreateAtlases( glyphs );
355 if( mImpl->mAtlasL8 )
357 actorL8 = MeshActor::New( mImpl->CreateMesh( glyphs, positions, Pixel::L8, mImpl->mAtlasL8 ) );
358 actorL8.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
360 ShaderEffect shader = BasicShader::New();
361 actorL8.SetShaderEffect( shader );
364 MeshActor actorBGRA8888;
365 if( mImpl->mAtlasBGRA8888 )
367 actorBGRA8888 = MeshActor::New( mImpl->CreateMesh( glyphs, positions, Pixel::BGRA8888, mImpl->mAtlasBGRA8888 ) );
368 actorBGRA8888.SetColorMode( USE_OWN_MULTIPLY_PARENT_COLOR );
370 ShaderEffect shader = BasicShader::New();
371 actorBGRA8888.SetShaderEffect( shader );
374 // If we have both monochrome & color glyphs, two mesh actors are returned in a container
375 if( actorL8 && actorBGRA8888 )
377 mImpl->mActor = ImageActor::New();
378 mImpl->mActor.Add( actorL8 );
379 mImpl->mActor.Add( actorBGRA8888 );
385 mImpl->mActor = actorL8;
387 else if( actorBGRA8888 )
389 mImpl->mActor = actorBGRA8888;
394 return mImpl->mActor;
397 BasicRenderer::BasicRenderer()
402 BasicRenderer::~BasicRenderer()