Merge "Add support for FontClient PreCache in candidate process" into devel/master
[platform/core/uifw/dali-adaptor.git] / third-party / glyphy / vector-font-cache.cpp
1 /*
2  * Copyright (c) 2019 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 <third-party/glyphy/vector-font-cache.h>
20
21 // EXTERNAL INCLUDES
22 #include <vector>
23 #include <math.h>
24
25 // INTERNAL INCLUDES
26 #include <third-party/glyphy/glyphy.h>
27 #include <third-party/glyphy/glyphy-freetype.h>
28
29 using namespace std;
30
31 namespace
32 {
33
34 const unsigned int INITIAL_GLYPH_CAPACITY = 50;
35 const double MIN_FONT_SIZE = 10;
36
37 static glyphy_bool_t
38 accumulate_endpoint( glyphy_arc_endpoint_t*         endpoint,
39                      vector<glyphy_arc_endpoint_t>* endpoints )
40 {
41   endpoints->push_back( *endpoint );
42   return true;
43 }
44
45 } // unnamed namespace
46
47 namespace Dali
48 {
49
50 namespace TextAbstraction
51 {
52
53 namespace Internal
54 {
55
56 typedef vector<VectorBlob> BlobArray;
57
58 struct VectorGlyph
59 {
60   /**
61    * @brief Create a vector-based glyph.
62    */
63   static VectorGlyph* New( FT_Face face,
64                            FontId fontId,
65                            GlyphIndex index,
66                            glyphy_arc_accumulator_t* accumulator )
67   {
68     VectorGlyph* newGlyph = new VectorGlyph();
69     newGlyph->blobData.resize( 1024 * 16 );
70
71     if( FT_Err_Ok != FT_Load_Glyph( face,
72                                     index,
73                                     FT_LOAD_NO_BITMAP |
74                                     FT_LOAD_NO_HINTING |
75                                     FT_LOAD_NO_AUTOHINT |
76                                     FT_LOAD_NO_SCALE |
77                                     FT_LOAD_LINEAR_DESIGN |
78                                     FT_LOAD_IGNORE_TRANSFORM))
79     {
80       DALI_LOG_ERROR( "FT_Load_Glyph failed\n" );
81       delete newGlyph;
82       return NULL;
83     }
84
85     const double upem = static_cast<double>( face->units_per_EM );
86     const double tolerance = upem * 1.0f/2048.0f;
87
88     glyphy_arc_accumulator_reset( accumulator);
89     glyphy_arc_accumulator_set_tolerance( accumulator, tolerance );
90
91     vector<glyphy_arc_endpoint_t> endpoints;
92     glyphy_arc_accumulator_set_callback( accumulator,
93                                          reinterpret_cast<glyphy_arc_endpoint_accumulator_callback_t>( accumulate_endpoint ),
94                                          &endpoints );
95
96     if( FT_Err_Ok != glyphy_freetype_outline_decompose( &face->glyph->outline, accumulator ) )
97     {
98       DALI_LOG_ERROR( "glyphy_freetype_outline_decompose failed\n" );
99       delete newGlyph;
100       return NULL;
101     }
102
103     DALI_ASSERT_DEBUG( glyphy_arc_accumulator_get_error(accumulator) <= tolerance && "glyphy_arc_accumulator_get_error > tolerance" );
104
105     if( endpoints.size() )
106     {
107       glyphy_outline_winding_from_even_odd( &endpoints[0], static_cast<unsigned int>( endpoints.size() ), false );
108     }
109
110     unsigned int blobLength( 0 );
111     double averageFetchAchieved( 0.0 );
112     if (!glyphy_arc_list_encode_blob( endpoints.size() ? &endpoints[0] : NULL,
113                                       static_cast<unsigned int>( endpoints.size() ),
114                                       &newGlyph->blobData[0],
115                                       static_cast<unsigned int>( newGlyph->blobData.capacity() ),
116                                       upem / ( MIN_FONT_SIZE * M_SQRT2 ),
117                                       4,
118                                       &averageFetchAchieved,
119                                       &blobLength,
120                                       &newGlyph->nominalWidth,
121                                       &newGlyph->nominalHeight,
122                                       &newGlyph->extents ) )
123     {
124       DALI_LOG_ERROR( "glyphy_arc_list_encode_blob failed\n" );
125       delete newGlyph;
126       return NULL;
127     }
128     newGlyph->blobData.resize( blobLength );
129
130     glyphy_extents_scale( &newGlyph->extents, 1.0/upem, 1.0/upem );
131
132     newGlyph->glyphInfo.fontId = fontId;
133     newGlyph->glyphInfo.index  = index;
134
135     if( glyphy_extents_is_empty( &newGlyph->extents ) )
136     {
137       newGlyph->glyphInfo.width  = 0.0f;
138       newGlyph->glyphInfo.height = 0.0f;
139
140       newGlyph->glyphInfo.xBearing = 0.0f;
141       newGlyph->glyphInfo.yBearing = 0.0f;
142     }
143     else
144     {
145       newGlyph->glyphInfo.width  = static_cast<float>( newGlyph->extents.max_x - newGlyph->extents.min_x );
146       newGlyph->glyphInfo.height = static_cast<float>( newGlyph->extents.max_y - newGlyph->extents.min_y );
147
148       newGlyph->glyphInfo.xBearing = static_cast<float>( newGlyph->extents.min_x );
149       newGlyph->glyphInfo.yBearing = newGlyph->glyphInfo.height + static_cast<float>( newGlyph->extents.min_y );
150     }
151
152     newGlyph->glyphInfo.advance = static_cast<float>( static_cast<double>( face->glyph->metrics.horiAdvance ) / upem );
153     newGlyph->glyphInfo.scaleFactor = 0.0f;
154
155     return newGlyph;
156   }
157
158   VectorGlyph()
159   : advance( 0.0 ),
160     nominalWidth( 0 ),
161     nominalHeight( 0 ),
162     glyphInfo(),
163     blobData()
164   {
165     glyphy_extents_clear( &extents );
166   }
167
168   glyphy_extents_t extents;
169   double           advance;
170   unsigned int     nominalWidth;
171   unsigned int     nominalHeight;
172   GlyphInfo        glyphInfo;
173   BlobArray        blobData;
174 };
175
176 typedef vector<VectorGlyph*> GlyphCache;
177
178 struct VectorFont
179 {
180   VectorFont( FT_Face face )
181   : mFace( face ),
182     mGlyphCache()
183   {
184     mGlyphCache.reserve( INITIAL_GLYPH_CAPACITY );
185   }
186
187   FT_Face    mFace;
188   GlyphCache mGlyphCache;
189 };
190
191 struct VectorFontCache::Impl
192 {
193   Impl( FT_Library freeTypeLibrary )
194   : mFreeTypeLibrary( freeTypeLibrary ),
195     mIdLookup(),
196     mVectorFonts(),
197     mAccumulator( NULL )
198   {
199     mAccumulator = glyphy_arc_accumulator_create();
200   }
201
202   ~Impl()
203   {
204     glyphy_arc_accumulator_destroy( mAccumulator );
205   }
206
207 private:
208
209   // Declared private and left undefined to avoid copies.
210   Impl( const Impl& );
211   // Declared private and left undefined to avoid copies.
212   Impl& operator=( const Impl& );
213
214 public:
215
216   FT_Library mFreeTypeLibrary; ///< A handle to a FreeType library instance.
217
218   vector<string> mIdLookup;
219
220   vector<VectorFont*> mVectorFonts;
221
222   glyphy_arc_accumulator_t* mAccumulator;
223 };
224
225 VectorFontCache::VectorFontCache( FT_Library freeTypeLibrary )
226 : mImpl( NULL )
227 {
228   mImpl = new Impl( freeTypeLibrary );
229 }
230
231 VectorFontCache::~VectorFontCache()
232 {
233   delete mImpl;
234 }
235
236 FontId VectorFontCache::GetFontId( const std::string& url )
237 {
238   FontId id( 0 );
239
240   if( mImpl )
241   {
242     if( ! FindFont( url, id ) )
243     {
244       id = CreateFont( url );
245     }
246   }
247
248   return id;
249 }
250
251 void VectorFontCache::GetGlyphMetrics( FontId vectorFontId, GlyphInfo& glyphInfo )
252 {
253   if( mImpl )
254   {
255     if( vectorFontId > 0 &&
256         vectorFontId-1 < mImpl->mVectorFonts.size() )
257     {
258       VectorFont* font = mImpl->mVectorFonts[ vectorFontId-1 ];
259       GlyphCache& cache = font->mGlyphCache;
260
261       bool foundGlyph( false );
262       unsigned int foundIndex( 0 );
263       for( unsigned int i=0; i<cache.size(); ++i )
264       {
265         VectorGlyph* glyph = cache[i];
266
267         if( glyph->glyphInfo.index == glyphInfo.index )
268         {
269           foundIndex = i;
270           foundGlyph = true;
271           break;
272         }
273       }
274
275       if( foundGlyph )
276       {
277         VectorGlyph* glyph = cache[foundIndex];
278         // Note - this clobbers the original fontId, but helps avoid duplicating identical blobs
279         // e.g. if when the same font family is requested in different point-sizes
280         glyphInfo = glyph->glyphInfo;
281       }
282       else
283       {
284         VectorGlyph* newGlyph = VectorGlyph::New( font->mFace,
285                                                   glyphInfo.fontId,
286                                                   glyphInfo.index,
287                                                   mImpl->mAccumulator );
288
289         if( newGlyph )
290         {
291           glyphInfo = newGlyph->glyphInfo;
292
293           cache.push_back( newGlyph );
294         }
295       }
296     }
297   }
298 }
299
300 void VectorFontCache::GetVectorBlob( FontId vectorFontId,
301                                      FontId fontId,
302                                      GlyphIndex glyphIndex,
303                                      VectorBlob*& blob,
304                                      unsigned int& blobLength,
305                                      unsigned int& nominalWidth,
306                                      unsigned int& nominalHeight )
307 {
308   if( mImpl )
309   {
310     if( vectorFontId > 0 &&
311         vectorFontId-1 < mImpl->mVectorFonts.size() )
312     {
313       VectorFont* font = mImpl->mVectorFonts[ vectorFontId-1 ];
314       GlyphCache& cache = font->mGlyphCache;
315
316       bool foundGlyph( false );
317       unsigned int foundIndex( 0 );
318       for( unsigned int i=0; i<cache.size(); ++i )
319       {
320         VectorGlyph* glyph = cache[i];
321
322         if( glyph->glyphInfo.index == glyphIndex )
323         {
324           foundIndex = i;
325           foundGlyph = true;
326           break;
327         }
328       }
329
330       if( foundGlyph )
331       {
332         VectorGlyph* glyph = cache[foundIndex];
333
334         blob          = &glyph->blobData[0];
335         blobLength    = static_cast<unsigned int>( glyph->blobData.size() );
336         nominalWidth  = glyph->nominalWidth;
337         nominalHeight = glyph->nominalHeight;
338       }
339       else
340       {
341         VectorGlyph* newGlyph = VectorGlyph::New( font->mFace, fontId, glyphIndex, mImpl->mAccumulator );
342
343         if( newGlyph )
344         {
345           blob          = &newGlyph->blobData[0];
346           blobLength    = static_cast<unsigned int>( newGlyph->blobData.size() );
347           nominalWidth  = newGlyph->nominalWidth;
348           nominalHeight = newGlyph->nominalHeight;
349
350           cache.push_back( newGlyph );
351         }
352       }
353     }
354   }
355 }
356
357 bool VectorFontCache::FindFont( const string& url, FontId& vectorFontId ) const
358 {
359   vectorFontId = 0u;
360
361   const vector<string>& idLookup = mImpl->mIdLookup;
362
363   for( unsigned int i=0; i<idLookup.size(); ++i, ++vectorFontId )
364   {
365     if( url == idLookup[i] )
366     {
367       ++vectorFontId;
368       return true;
369     }
370   }
371
372   return false;
373 }
374
375 FontId VectorFontCache::CreateFont( const string& url )
376 {
377   FontId id( 0 );
378
379   // Create & cache new font face
380   FT_Face face;
381   int error = FT_New_Face( mImpl->mFreeTypeLibrary,
382                            url.c_str(),
383                            0,
384                            &face );
385
386   if( FT_Err_Ok == error )
387   {
388     mImpl->mIdLookup.push_back( url );
389     id = static_cast<FontId>( mImpl->mIdLookup.size() );
390
391     VectorFont* newFont = new VectorFont( face );
392     mImpl->mVectorFonts.push_back( newFont );
393
394     DALI_ASSERT_DEBUG( mImpl->mIdLookup.size() == mImpl->mVectorFonts.size() );
395   }
396
397   return id;
398 }
399
400 } // namespace Internal
401
402 } // namespace TextAbstraction
403
404 } // namespace Dali