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