Merge "Remove orientation from application class" into tizen
[platform/core/uifw/dali-adaptor.git] / text / dali / internal / text-abstraction / shaping-impl.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 "shaping-impl.h"
20
21 // INTERNAL INCLUDES
22 #include <singleton-service-impl.h>
23 #include <dali/public-api/text-abstraction/font-client.h>
24 #include <dali/public-api/text-abstraction/glyph-info.h>
25 #include <dali/integration-api/debug.h>
26
27 // EXTERNAL INCLUDES
28 #include <harfbuzz/hb.h>
29 #include <harfbuzz/hb-ft.h>
30
31 #include <ft2build.h>
32
33 namespace Dali
34 {
35
36 namespace TextAbstraction
37 {
38
39 namespace Internal
40 {
41
42 const char*        DEFAULT_LANGUAGE = "en";
43 const unsigned int DEFAULT_LANGUAGE_LENGTH = 2u;
44 const float        FROM_266 = 1.0f / 64.0f;
45
46 const hb_script_t SCRIPT_TO_HARFBUZZ[] =
47 {
48   HB_SCRIPT_LATIN,
49   HB_SCRIPT_ARABIC,
50   HB_SCRIPT_DEVANAGARI,
51   HB_SCRIPT_BENGALI,
52   HB_SCRIPT_GURMUKHI,
53   HB_SCRIPT_GUJARATI,
54   HB_SCRIPT_ORIYA,
55   HB_SCRIPT_TAMIL,
56   HB_SCRIPT_TELUGU,
57   HB_SCRIPT_KANNADA,
58   HB_SCRIPT_MALAYALAM,
59   HB_SCRIPT_SINHALA,
60   HB_SCRIPT_HAN,
61   HB_SCRIPT_HANGUL,
62   HB_SCRIPT_KHMER,
63   HB_SCRIPT_LAO,
64   HB_SCRIPT_THAI,
65   HB_SCRIPT_MYANMAR,
66   HB_SCRIPT_UNKNOWN
67 };
68
69 struct Shaping::Plugin
70 {
71   Plugin()
72   : mFreeTypeLibrary( NULL ),
73     mIndices(),
74     mAdvance(),
75     mCharacterMap(),
76     mFontId( 0u )
77   {
78   }
79
80   ~Plugin()
81   {
82     FT_Done_FreeType( mFreeTypeLibrary );
83   }
84
85   void Initialize()
86   {
87     int error = FT_Init_FreeType( &mFreeTypeLibrary );
88     if( FT_Err_Ok != error )
89     {
90       DALI_LOG_ERROR( "FreeType Init error: %d\n", error );
91     }
92   }
93
94   Length Shape( const Character* const text,
95                 Length numberOfCharacters,
96                 FontId fontId,
97                 Script script )
98   {
99     // Clear previoursly shaped texts.
100     mIndices.Clear();
101     mAdvance.Clear();
102     mCharacterMap.Clear();
103     mOffset.Clear();
104     mFontId = fontId;
105
106     // Reserve some space to avoid reallocations.
107     const Length numberOfGlyphs = static_cast<Length>( 1.3f * static_cast<float>( numberOfCharacters ) );
108     mIndices.Reserve( numberOfGlyphs );
109     mAdvance.Reserve( numberOfGlyphs );
110     mCharacterMap.Reserve( numberOfGlyphs );
111     mOffset.Reserve( 2u * numberOfGlyphs );
112
113     TextAbstraction::FontClient fontClient = TextAbstraction::FontClient::Get();
114
115     // Get the font's path file name from the font Id.
116     FontDescription fontDescription;
117     fontClient.GetDescription( fontId, fontDescription );
118
119     // Create a FreeType font's face.
120     FT_Face face;
121     FT_Error retVal = FT_New_Face( mFreeTypeLibrary, fontDescription.path.c_str(), 0u, &face );
122     if( FT_Err_Ok != retVal )
123     {
124       DALI_LOG_ERROR( "Failed to open face: %s\n", fontDescription.path.c_str() );
125       return 0u;
126     }
127
128     unsigned int horizontalDpi = 0u;
129     unsigned int verticalDpi = 0u;
130     fontClient.GetDpi( horizontalDpi, verticalDpi );
131
132     FT_Set_Char_Size( face,
133                       0u,
134                       fontClient.GetPointSize( fontId ),
135                       horizontalDpi,
136                       verticalDpi );
137
138     /* Get our harfbuzz font struct */
139     hb_font_t* harfBuzzFont;
140     harfBuzzFont = hb_ft_font_create( face, NULL );
141
142     /* Create a buffer for harfbuzz to use */
143     hb_buffer_t* harfBuzzBuffer = hb_buffer_create();
144
145     const bool rtlDirection = IsRightToLeftScript( script );
146     hb_buffer_set_direction( harfBuzzBuffer,
147                              rtlDirection ? HB_DIRECTION_RTL : HB_DIRECTION_LTR ); /* or LTR */
148
149     hb_buffer_set_script( harfBuzzBuffer,
150                           SCRIPT_TO_HARFBUZZ[ script ] ); /* see hb-unicode.h */
151
152     hb_buffer_set_language( harfBuzzBuffer,
153                             hb_language_from_string( DEFAULT_LANGUAGE,
154                                                      DEFAULT_LANGUAGE_LENGTH ) );
155
156     /* Layout the text */
157     hb_buffer_add_utf32( harfBuzzBuffer, text, numberOfCharacters, 0u, numberOfCharacters );
158
159     hb_shape( harfBuzzFont, harfBuzzBuffer, NULL, 0u );
160
161     /* Get glyph data */
162     unsigned int glyphCount;
163     hb_glyph_info_t* glyphInfo = hb_buffer_get_glyph_infos( harfBuzzBuffer, &glyphCount );
164     hb_glyph_position_t *glyphPositions = hb_buffer_get_glyph_positions( harfBuzzBuffer, &glyphCount );
165     const GlyphIndex lastGlyphIndex = glyphCount - 1u;
166     for( GlyphIndex i = 0u; i < glyphCount; )
167     {
168       if( rtlDirection )
169       {
170         // If the direction is right to left, Harfbuzz retrieves the glyphs in the visual order.
171         // The glyphs are needed in the logical order to layout the text in lines.
172         // Do not change the order of the glyphs if they belong to the same cluster.
173         GlyphIndex rtlIndex = lastGlyphIndex - i;
174
175         unsigned int cluster = glyphInfo[rtlIndex].cluster;
176         unsigned int previousCluster = cluster;
177         Length numberOfGlyphsInCluster = 0u;
178
179         while( ( cluster == previousCluster ) )
180         {
181           ++numberOfGlyphsInCluster;
182           previousCluster = cluster;
183
184           if( rtlIndex > 0u )
185           {
186             --rtlIndex;
187
188             cluster = glyphInfo[rtlIndex].cluster;
189           }
190           else
191           {
192             break;
193           }
194         }
195
196         rtlIndex = lastGlyphIndex - ( i + ( numberOfGlyphsInCluster - 1u ) );
197
198         for( GlyphIndex j = 0u; j < numberOfGlyphsInCluster; ++j )
199         {
200           const GlyphIndex index = rtlIndex + j;
201
202           mIndices.PushBack( glyphInfo[index].codepoint );
203           mAdvance.PushBack( floor( glyphPositions[index].x_advance * FROM_266 ) );
204           mCharacterMap.PushBack( glyphInfo[index].cluster );
205           mOffset.PushBack( floor( glyphPositions[index].x_offset * FROM_266 ) );
206           mOffset.PushBack( floor( glyphPositions[index].y_offset * FROM_266 ) );
207         }
208
209         i += numberOfGlyphsInCluster;
210       }
211       else
212       {
213         mIndices.PushBack( glyphInfo[i].codepoint );
214         mAdvance.PushBack( floor( glyphPositions[i].x_advance * FROM_266 ) );
215         mCharacterMap.PushBack( glyphInfo[i].cluster );
216         mOffset.PushBack( floor( glyphPositions[i].x_offset * FROM_266 ) );
217         mOffset.PushBack( floor( glyphPositions[i].y_offset * FROM_266 ) );
218
219         ++i;
220       }
221     }
222
223     /* Cleanup */
224     hb_buffer_destroy( harfBuzzBuffer );
225     hb_font_destroy( harfBuzzFont );
226     FT_Done_Face( face );
227
228     return mIndices.Count();
229   }
230
231   void GetGlyphs( GlyphInfo* glyphInfo,
232                   CharacterIndex* glyphToCharacterMap )
233   {
234     Vector<CharacterIndex>::ConstIterator indicesIt = mIndices.Begin();
235     Vector<float>::ConstIterator advanceIt = mAdvance.Begin();
236     Vector<float>::ConstIterator offsetIt = mOffset.Begin();
237     Vector<CharacterIndex>::ConstIterator characterMapIt = mCharacterMap.Begin();
238
239     for( GlyphIndex index = 0u, size = mIndices.Count(); index < size; ++index )
240     {
241       GlyphInfo& glyph = *( glyphInfo + index );
242       CharacterIndex& glyphToCharacter = *( glyphToCharacterMap + index );
243
244       glyph.fontId = mFontId;
245       glyph.index = *( indicesIt + index );
246       glyph.advance = *( advanceIt + index );
247
248       const GlyphIndex offsetIndex = 2u * index;
249       glyph.xBearing = *( offsetIt + offsetIndex );
250       glyph.yBearing = *( offsetIt + offsetIndex + 1u );
251
252       glyphToCharacter = *( characterMapIt + index );
253     }
254   }
255
256   FT_Library             mFreeTypeLibrary;
257
258   Vector<CharacterIndex> mIndices;
259   Vector<float>          mAdvance;
260   Vector<float>          mOffset;
261   Vector<CharacterIndex> mCharacterMap;
262   FontId                 mFontId;
263 };
264
265 Shaping::Shaping()
266 : mPlugin( NULL )
267 {
268 }
269
270 Shaping::~Shaping()
271 {
272   delete mPlugin;
273 }
274
275 TextAbstraction::Shaping Shaping::Get()
276 {
277   TextAbstraction::Shaping shapingHandle;
278
279   SingletonService service( SingletonService::Get() );
280   if( service )
281   {
282     // Check whether the singleton is already created
283     Dali::BaseHandle handle = service.GetSingleton( typeid( TextAbstraction::Shaping ) );
284     if( handle )
285     {
286       // If so, downcast the handle
287       Shaping* impl = dynamic_cast< Internal::Shaping* >( handle.GetObjectPtr() );
288       shapingHandle = TextAbstraction::Shaping( impl );
289     }
290     else // create and register the object
291     {
292       shapingHandle = TextAbstraction::Shaping( new Shaping );
293       service.Register( typeid( shapingHandle ), shapingHandle );
294     }
295   }
296
297   return shapingHandle;
298 }
299
300 Length Shaping::Shape( const Character* const text,
301                        Length numberOfCharacters,
302                        FontId fontId,
303                        Script script )
304 {
305   CreatePlugin();
306
307   return mPlugin->Shape( text,
308                          numberOfCharacters,
309                          fontId,
310                          script );
311 }
312
313 void Shaping::GetGlyphs( GlyphInfo* glyphInfo,
314                          CharacterIndex* glyphToCharacterMap )
315 {
316   CreatePlugin();
317
318   mPlugin->GetGlyphs( glyphInfo,
319                       glyphToCharacterMap );
320 }
321
322 void Shaping::CreatePlugin()
323 {
324   if( !mPlugin )
325   {
326     mPlugin = new Plugin();
327     mPlugin->Initialize();
328   }
329 }
330
331 } // namespace Internal
332
333 } // namespace TextAbstraction
334
335 } // namespace Dali