[Tizen] Ensure join of font thread
[platform/core/uifw/dali-adaptor.git] / dali / devel-api / text-abstraction / font-client.cpp
1 /*
2  * Copyright (c) 2022 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/devel-api/text-abstraction/font-client.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/internal/imaging/common/image-operations.h>
23 #include <dali/internal/text/text-abstraction/font-client-impl.h>
24
25 namespace Dali
26 {
27 namespace TextAbstraction
28 {
29 const PointSize26Dot6 FontClient::DEFAULT_POINT_SIZE   = 768u;                           // 12*64
30 const float           FontClient::DEFAULT_ITALIC_ANGLE = 12.f * Dali::Math::PI_OVER_180; // FreeType documentation states the software italic is done by doing a horizontal shear of 12 degrees (file ftsynth.h).
31
32 //Default atlas block
33 const bool     FontClient::DEFAULT_ATLAS_LIMITATION_ENABLED = true;
34 const uint32_t FontClient::DEFAULT_TEXT_ATLAS_WIDTH         = 512u;
35 const uint32_t FontClient::DEFAULT_TEXT_ATLAS_HEIGHT        = 512u;
36 const Size     FontClient::DEFAULT_TEXT_ATLAS_SIZE(DEFAULT_TEXT_ATLAS_WIDTH, DEFAULT_TEXT_ATLAS_HEIGHT);
37
38 //Maximum atlas block
39 const uint32_t FontClient::MAX_TEXT_ATLAS_WIDTH  = 1024u;
40 const uint32_t FontClient::MAX_TEXT_ATLAS_HEIGHT = 1024u;
41 const Size     FontClient::MAX_TEXT_ATLAS_SIZE(MAX_TEXT_ATLAS_WIDTH, MAX_TEXT_ATLAS_HEIGHT);
42
43 //MAX_WIDTH_FIT_IN_ATLAS: blockWidth + 2 * DOUBLE_PIXEL_PADDING + 1u <= atlasWidth
44 //MAX_HEIGHT_FIT_IN_ATLAS: blockHeight + 2 * DOUBLE_PIXEL_PADDING + 1u <= atlasHeight
45 const uint16_t FontClient::PADDING_TEXT_ATLAS_BLOCK = 5u; // 2 * DOUBLE_PIXEL_PADDING + 1u
46
47 //Maximum block size to fit into atlas block
48 const Size FontClient::MAX_SIZE_FIT_IN_ATLAS(MAX_TEXT_ATLAS_WIDTH - PADDING_TEXT_ATLAS_BLOCK, MAX_TEXT_ATLAS_HEIGHT - PADDING_TEXT_ATLAS_BLOCK);
49
50 const uint32_t FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE = 64u; //Found this value from toolkit
51
52 // FontClient::GlyphBufferData
53
54 FontClient::GlyphBufferData::GlyphBufferData()
55 : buffer{nullptr},
56   width{0u},
57   height{0u},
58   outlineOffsetX{0},
59   outlineOffsetY{0},
60   format{Pixel::A8},
61   compressionType{CompressionType::NO_COMPRESSION},
62   isColorEmoji{false},
63   isColorBitmap{false},
64   isBufferOwned{false}
65 {
66 }
67
68 FontClient::GlyphBufferData::~GlyphBufferData()
69 {
70   if(isBufferOwned)
71   {
72     free(buffer);
73   }
74 }
75
76 FontClient::GlyphBufferData::GlyphBufferData(FontClient::GlyphBufferData&& rhs) noexcept
77 : buffer{rhs.buffer},
78   width{rhs.width},
79   height{rhs.height},
80   outlineOffsetX{rhs.outlineOffsetX},
81   outlineOffsetY{rhs.outlineOffsetY},
82   format{rhs.format},
83   compressionType{rhs.compressionType},
84   isColorEmoji{rhs.isColorEmoji},
85   isColorBitmap{rhs.isColorBitmap},
86   isBufferOwned{rhs.isBufferOwned}
87 {
88   // Remove moved data
89   rhs.buffer        = nullptr;
90   rhs.isBufferOwned = false;
91 }
92
93 FontClient::GlyphBufferData& FontClient::GlyphBufferData::operator=(FontClient::GlyphBufferData&& rhs) noexcept
94 {
95   buffer          = rhs.buffer;
96   width           = rhs.width;
97   height          = rhs.height;
98   outlineOffsetX  = rhs.outlineOffsetX;
99   outlineOffsetY  = rhs.outlineOffsetY;
100   format          = rhs.format;
101   compressionType = rhs.compressionType;
102   isColorEmoji    = rhs.isColorEmoji;
103   isColorBitmap   = rhs.isColorBitmap;
104   isBufferOwned   = rhs.isBufferOwned;
105
106   // Remove moved data
107   rhs.buffer        = nullptr;
108   rhs.isBufferOwned = false;
109
110   return *this;
111 }
112
113 size_t FontClient::GlyphBufferData::Compress(const uint8_t* const __restrict__ inBuffer, GlyphBufferData& __restrict__ outBufferData)
114 {
115   size_t bufferSize                       = 0u;
116   uint8_t*& __restrict__ compressedBuffer = outBufferData.buffer;
117   switch(outBufferData.compressionType)
118   {
119     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
120     {
121       bufferSize = static_cast<size_t>(outBufferData.width) * static_cast<size_t>(outBufferData.height) * static_cast<size_t>(Pixel::GetBytesPerPixel(outBufferData.format));
122
123       compressedBuffer = (uint8_t*)malloc(bufferSize);
124       if(DALI_UNLIKELY(compressedBuffer == nullptr))
125       {
126         return 0u;
127       }
128       outBufferData.isBufferOwned = true;
129
130       // Copy buffer without compress
131       memcpy(compressedBuffer, inBuffer, bufferSize);
132       break;
133     }
134     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
135     {
136       const uint32_t widthByte       = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
137       const uint32_t componentCount  = (widthByte >> 1);
138       const bool     considerPadding = (widthByte & 1) ? true : false;
139
140       // For BIT_PER_PIXEL_4 type, we can know final compressed buffer size immediatly.
141       bufferSize       = static_cast<size_t>(outBufferData.height) * static_cast<size_t>(componentCount + (considerPadding ? 1 : 0));
142       compressedBuffer = (uint8_t*)malloc(bufferSize);
143       if(DALI_UNLIKELY(compressedBuffer == nullptr))
144       {
145         return 0u;
146       }
147       outBufferData.isBufferOwned = true;
148
149       uint8_t* __restrict__ outBufferPtr      = compressedBuffer;
150       const uint8_t* __restrict__ inBufferPtr = inBuffer;
151
152       // Compress for each line
153       for(uint32_t y = 0; y < outBufferData.height; ++y)
154       {
155         for(uint32_t x = 0; x < componentCount; ++x)
156         {
157           const uint8_t v0 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
158           const uint8_t v1 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
159
160           *(outBufferPtr++) = (v0 << 4) | v1;
161         }
162         if(considerPadding)
163         {
164           *(outBufferPtr++) = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
165         }
166       }
167       break;
168     }
169     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
170     {
171       const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
172
173       // Allocate temperal buffer. Note that RLE4 can be bigger than original buffer.
174       uint8_t* __restrict__ tempBuffer = (uint8_t*)malloc(outBufferData.height * (widthByte + 1));
175       if(DALI_UNLIKELY(tempBuffer == nullptr))
176       {
177         return 0u;
178       }
179
180       uint8_t* __restrict__ outBufferPtr      = tempBuffer;
181       const uint8_t* __restrict__ inBufferPtr = inBuffer;
182
183       bufferSize = 0u;
184
185       // Compress for each line
186       for(uint32_t y = 0; y < outBufferData.height; ++y)
187       {
188         uint32_t encodedByte = 0;
189         while(encodedByte < widthByte)
190         {
191           // Case 1 : Remain only 1 byte
192           if(DALI_UNLIKELY(encodedByte + 1 == widthByte))
193           {
194             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
195             const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
196             *(outBufferPtr++)   = v0;
197             ++encodedByte;
198             ++bufferSize;
199           }
200           // Case 2 : Remain only 2 byte
201           else if(DALI_UNLIKELY(encodedByte + 2 == widthByte))
202           {
203             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
204             const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
205             const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
206             const uint8_t v1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
207             encodedByte += 2;
208             if(v0 == v1)
209             {
210               *(outBufferPtr++) = 0x80 | v0;
211               ++bufferSize;
212             }
213             else
214             {
215               *(outBufferPtr++) = 0x10 | v0;
216               *(outBufferPtr++) = v1 << 4;
217               bufferSize += 2;
218             }
219           }
220           // Case 3 : Normal case. Remain byte bigger or equal than 3.
221           else
222           {
223             // Compress rule -
224             // Read 2 byte as v0 and v1.
225             // - If v0 == v1, We can compress. mark the first bit as 1. and remain 3 bit mark as the "runLength - 2".
226             //   runLength can be maximum 9.
227             // - If v0 != v1, We cannot compress. mark the first bit as 0. and remain 3 bit mark as the "(nonRunLength - 1) / 2"
228             //   Due to the BitPerPixel is 4, nonRunLength should be odd value.
229             //   nonRunLength cutted if v0 == v1.
230             //   nonRunLength can be maximum 15.
231
232             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
233             const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
234             const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
235             const uint8_t v1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
236             encodedByte += 2;
237             // We can compress by RLE
238             if(v0 == v1)
239             {
240               uint8_t runLength = 2;
241               while(encodedByte < widthByte && runLength < 9)
242               {
243                 const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
244                 const uint8_t v2    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prev2) & 0x0f; // Intented underflow
245                 if(v2 == v0)
246                 {
247                   ++inBufferPtr;
248                   ++encodedByte;
249                   ++runLength;
250                 }
251                 else
252                 {
253                   break;
254                 }
255               }
256
257               // Update (runLength - 2) result.
258               *(outBufferPtr++) = ((0x8 | (runLength - 2)) << 4) | v0;
259               ++bufferSize;
260             }
261             // We cannot compress by RLE.
262             else
263             {
264               // Read one more value.
265               const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
266               const uint8_t v2    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev2) & 0x0f; // Intented underflow
267               ++encodedByte;
268
269               uint8_t  nonRunLength          = 3;
270               uint8_t* nonRunLengthHeaderPtr = outBufferPtr;
271               *(outBufferPtr++)              = v0;
272               *(outBufferPtr++)              = (v1 << 4) | v2;
273               bufferSize += 2;
274               while(encodedByte < widthByte && nonRunLength < 15)
275               {
276                 if(DALI_LIKELY(encodedByte + 1 < widthByte))
277                 {
278                   const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
279                   const uint8_t w0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
280                   const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1 - widthByte));
281                   const uint8_t w1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1)) - prew1) & 0x0f; // Intented underflow
282                   if(w0 == w1)
283                   {
284                     // Stop non-compress logic.
285                     break;
286                   }
287                   else
288                   {
289                     ++bufferSize;
290                     *(outBufferPtr++) = (w0 << 4) | w1;
291                     inBufferPtr += 2;
292                     encodedByte += 2;
293                     nonRunLength += 2;
294                   }
295                 }
296                 else
297                 {
298                   // Edge case. There is only one pixel remained.
299                   const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
300                   const uint8_t w0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
301                   {
302                     ++bufferSize;
303                     *(outBufferPtr++) = (w0 << 4);
304                     ++encodedByte;
305                     ++inBufferPtr;
306                     // Increase nonRunLength 2 even latest value is invalid.
307                     nonRunLength += 2;
308                   }
309                 }
310               }
311
312               // Update (nonRunLength-1)/2 result into header.
313               *(nonRunLengthHeaderPtr) |= (nonRunLength >> 1) << 4;
314             }
315           }
316         }
317       }
318
319       // Allocate and copy data
320       compressedBuffer = (uint8_t*)malloc(bufferSize);
321       if(DALI_UNLIKELY(compressedBuffer == nullptr))
322       {
323         free(tempBuffer);
324         return 0u;
325       }
326       outBufferData.isBufferOwned = true;
327
328       memcpy(compressedBuffer, tempBuffer, bufferSize);
329       free(tempBuffer);
330
331       break;
332     }
333     default:
334     {
335       break;
336     }
337   }
338
339   return bufferSize;
340 }
341
342 void FontClient::GlyphBufferData::Decompress(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer)
343 {
344   if(DALI_UNLIKELY(outBuffer == nullptr))
345   {
346     return;
347   }
348
349   switch(inBufferData.compressionType)
350   {
351     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
352     {
353       const auto bufferSize = inBufferData.width * inBufferData.height * Pixel::GetBytesPerPixel(inBufferData.format);
354
355       // Copy buffer without compress
356       memcpy(outBuffer, inBufferData.buffer, bufferSize);
357       break;
358     }
359     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
360     {
361       const uint32_t widthByte       = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
362       const uint32_t componentCount  = (widthByte >> 1);
363       const bool     considerPadding = (widthByte & 1) ? true : false;
364
365       uint8_t* __restrict__ outBufferPtr      = outBuffer;
366       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
367
368       // Compress for each line
369       for(uint32_t y = 0; y < inBufferData.height; ++y)
370       {
371         for(uint32_t x = 0; x < componentCount; ++x)
372         {
373           const uint8_t v  = *(inBufferPtr++);
374           const uint8_t v0 = (v >> 4) & 0x0f;
375           const uint8_t v1 = v & 0x0f;
376
377           *(outBufferPtr++) = (v0 << 4) | v0;
378           *(outBufferPtr++) = (v1 << 4) | v1;
379         }
380         if(considerPadding)
381         {
382           const uint8_t v   = *(inBufferPtr++);
383           *(outBufferPtr++) = (v << 4) | v;
384         }
385       }
386       break;
387     }
388     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
389     {
390       const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
391
392       uint8_t* __restrict__ outBufferPtr      = outBuffer;
393       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
394       // Compress for each line
395       for(uint32_t y = 0; y < inBufferData.height; ++y)
396       {
397         uint32_t x           = 0;
398         uint32_t decodedByte = 0;
399         while(decodedByte < widthByte)
400         {
401           const uint8_t v = *(inBufferPtr++);
402           ++x;
403           // Compress by RLE
404           if(v & 0x80)
405           {
406             const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
407             decodedByte += runLength;
408             const uint8_t repeatValue = v & 0x0f;
409             for(uint8_t iter = 0; iter < runLength; ++iter)
410             {
411               const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
412               const uint8_t v0    = (prev0 + repeatValue) & 0x0f;
413               *(outBufferPtr++)   = (v0 << 4) | v0;
414             }
415           }
416           // Not compress by RLE
417           else
418           {
419             const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
420             decodedByte += nonRunLength;
421             // First value.
422             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
423             const uint8_t v0    = (prev0 + (v & 0x0f)) & 0x0f;
424             *(outBufferPtr++)   = (v0 << 4) | v0;
425
426             const bool ignoreLastValue = decodedByte > widthByte ? true : false;
427             if(DALI_UNLIKELY(ignoreLastValue))
428             {
429               --decodedByte;
430               for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
431               {
432                 const uint8_t w     = *(inBufferPtr++);
433                 const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
434                 const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
435                 const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
436                 const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
437                 ++x;
438
439                 *(outBufferPtr++) = (w0 << 4) | w0;
440                 *(outBufferPtr++) = (w1 << 4) | w1;
441               }
442               // Last value.
443               {
444                 const uint8_t w     = ((*(inBufferPtr++)) >> 4) & 0x0f;
445                 const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
446                 const uint8_t w0    = (prew0 + w) & 0x0f;
447                 ++x;
448
449                 *(outBufferPtr++) = (w0 << 4) | w0;
450               }
451             }
452             else
453             {
454               for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
455               {
456                 const uint8_t w     = *(inBufferPtr++);
457                 const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
458                 const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
459                 const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
460                 const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
461                 ++x;
462
463                 *(outBufferPtr++) = (w0 << 4) | w0;
464                 *(outBufferPtr++) = (w1 << 4) | w1;
465               }
466             }
467           }
468         }
469       }
470       break;
471     }
472     default:
473     {
474       break;
475     }
476   }
477 }
478
479 void FontClient::GlyphBufferData::DecompressScanline(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer, uint32_t& __restrict__ offset)
480 {
481   switch(inBufferData.compressionType)
482   {
483     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
484     {
485       const auto bufferSize = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
486
487       // Copy buffer without compress
488       memcpy(outBuffer, inBufferData.buffer + offset, bufferSize);
489
490       // Update offset
491       offset += bufferSize;
492       break;
493     }
494     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
495     {
496       const uint32_t widthByte       = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
497       const uint32_t componentCount  = (widthByte >> 1);
498       const bool     considerPadding = (widthByte & 1) ? true : false;
499
500       uint8_t* __restrict__ outBufferPtr      = outBuffer;
501       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
502
503       // Decompress scanline
504       for(uint32_t x = 0; x < componentCount; ++x)
505       {
506         const uint8_t v  = *(inBufferPtr++);
507         const uint8_t v0 = (v >> 4) & 0x0f;
508         const uint8_t v1 = v & 0x0f;
509
510         *(outBufferPtr++) = (v0 << 4) | v0;
511         *(outBufferPtr++) = (v1 << 4) | v1;
512       }
513       if(considerPadding)
514       {
515         const uint8_t v   = *(inBufferPtr++);
516         *(outBufferPtr++) = (v << 4) | v;
517       }
518
519       // Update offset
520       offset += (widthByte + 1u) >> 1u;
521       break;
522     }
523     case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
524     {
525       const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
526
527       uint8_t* __restrict__ outBufferPtr      = outBuffer;
528       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
529
530       // If offset is zero, fill outBuffer as 0 first.
531       if(DALI_UNLIKELY(offset == 0))
532       {
533         memset(outBufferPtr, 0, widthByte);
534       }
535
536       // Decompress scanline
537       uint32_t decodedByte = 0;
538       while(decodedByte < widthByte)
539       {
540         const uint8_t v = *(inBufferPtr++);
541         ++offset;
542         // Compress by RLE
543         if(v & 0x80)
544         {
545           const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
546           decodedByte += runLength;
547           const uint8_t repeatValue = (v & 0x0f);
548           for(uint8_t iter = 0; iter < runLength; ++iter)
549           {
550             const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
551             const uint8_t v0    = (prev0 + repeatValue) & 0x0f;
552             *(outBufferPtr++)   = (v0 << 4) | v0;
553           }
554         }
555         // Not compress by RLE
556         else
557         {
558           const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
559           decodedByte += nonRunLength;
560           // First value.
561           const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
562           const uint8_t v0    = (prev0 + (v & 0x0f)) & 0x0f;
563           *(outBufferPtr++)   = (v0 << 4) | v0;
564
565           const bool ignoreLastValue = decodedByte > widthByte ? true : false;
566           if(DALI_UNLIKELY(ignoreLastValue))
567           {
568             --decodedByte;
569             for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
570             {
571               const uint8_t w     = *(inBufferPtr++);
572               const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
573               const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
574               const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
575               const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
576               ++offset;
577
578               *(outBufferPtr++) = (w0 << 4) | w0;
579               *(outBufferPtr++) = (w1 << 4) | w1;
580             }
581             // Last value.
582             {
583               const uint8_t w     = ((*(inBufferPtr++)) >> 4) & 0x0f;
584               const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
585               const uint8_t w0    = (prew0 + w) & 0x0f;
586               ++offset;
587
588               *(outBufferPtr++) = (w0 << 4) | w0;
589             }
590           }
591           else
592           {
593             for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
594             {
595               const uint8_t w     = *(inBufferPtr++);
596               const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
597               const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
598               const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
599               const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
600               ++offset;
601
602               *(outBufferPtr++) = (w0 << 4) | w0;
603               *(outBufferPtr++) = (w1 << 4) | w1;
604             }
605           }
606         }
607       }
608       break;
609     }
610     default:
611     {
612       break;
613     }
614   }
615 }
616
617 // FontClient
618
619 FontClient FontClient::Get()
620 {
621   return Internal::FontClient::Get();
622 }
623
624 FontClient::FontClient()
625 {
626 }
627
628 FontClient::~FontClient()
629 {
630 }
631
632 FontClient::FontClient(const FontClient& handle) = default;
633
634 FontClient& FontClient::operator=(const FontClient& handle) = default;
635
636 FontClient::FontClient(FontClient&& handle) = default;
637
638 FontClient& FontClient::operator=(FontClient&& handle) = default;
639
640 void FontClient::ClearCache()
641 {
642   GetImplementation(*this).ClearCache();
643 }
644
645 void FontClient::SetDpi(unsigned int horizontalDpi, unsigned int verticalDpi)
646 {
647   GetImplementation(*this).SetDpi(horizontalDpi, verticalDpi);
648 }
649
650 void FontClient::GetDpi(unsigned int& horizontalDpi, unsigned int& verticalDpi)
651 {
652   GetImplementation(*this).GetDpi(horizontalDpi, verticalDpi);
653 }
654
655 int FontClient::GetDefaultFontSize()
656 {
657   return GetImplementation(*this).GetDefaultFontSize();
658 }
659
660 void FontClient::ResetSystemDefaults()
661 {
662   GetImplementation(*this).ResetSystemDefaults();
663 }
664
665 void FontClient::GetDefaultFonts(FontList& defaultFonts)
666 {
667   GetImplementation(*this).GetDefaultFonts(defaultFonts);
668 }
669
670 void FontClient::InitDefaultFontDescription()
671 {
672   GetImplementation(*this).InitDefaultFontDescription();
673 }
674
675 void FontClient::GetDefaultPlatformFontDescription(FontDescription& fontDescription)
676 {
677   GetImplementation(*this).GetDefaultPlatformFontDescription(fontDescription);
678 }
679
680 void FontClient::GetSystemFonts(FontList& systemFonts)
681 {
682   GetImplementation(*this).GetSystemFonts(systemFonts);
683 }
684
685 void FontClient::GetDescription(FontId fontId, FontDescription& fontDescription)
686 {
687   GetImplementation(*this).GetDescription(fontId, fontDescription);
688 }
689
690 PointSize26Dot6 FontClient::GetPointSize(FontId fontId)
691 {
692   return GetImplementation(*this).GetPointSize(fontId);
693 }
694
695 bool FontClient::IsCharacterSupportedByFont(FontId fontId, Character character)
696 {
697   return GetImplementation(*this).IsCharacterSupportedByFont(fontId, character);
698 }
699
700 FontId FontClient::FindDefaultFont(Character       charcode,
701                                    PointSize26Dot6 requestedPointSize,
702                                    bool            preferColor)
703 {
704   return GetImplementation(*this).FindDefaultFont(charcode,
705                                                   requestedPointSize,
706                                                   preferColor);
707 }
708
709 FontId FontClient::FindFallbackFont(Character              charcode,
710                                     const FontDescription& preferredFontDescription,
711                                     PointSize26Dot6        requestedPointSize,
712                                     bool                   preferColor)
713 {
714   return GetImplementation(*this).FindFallbackFont(charcode, preferredFontDescription, requestedPointSize, preferColor);
715 }
716
717 FontId FontClient::GetFontId(const FontPath& path, PointSize26Dot6 requestedPointSize, FaceIndex faceIndex)
718 {
719   return GetImplementation(*this).GetFontId(path, requestedPointSize, faceIndex);
720 }
721
722 FontId FontClient::GetFontId(const FontDescription& fontDescription,
723                              PointSize26Dot6        requestedPointSize,
724                              FaceIndex              faceIndex)
725 {
726   return GetImplementation(*this).GetFontId(fontDescription,
727                                             requestedPointSize,
728                                             faceIndex);
729 }
730
731 FontId FontClient::GetFontId(const BitmapFont& bitmapFont)
732 {
733   return GetImplementation(*this).GetFontId(bitmapFont);
734 }
735
736 bool FontClient::IsScalable(const FontPath& path)
737 {
738   return GetImplementation(*this).IsScalable(path);
739 }
740
741 bool FontClient::IsScalable(const FontDescription& fontDescription)
742 {
743   return GetImplementation(*this).IsScalable(fontDescription);
744 }
745
746 void FontClient::GetFixedSizes(const FontPath& path, Dali::Vector<PointSize26Dot6>& sizes)
747 {
748   GetImplementation(*this).GetFixedSizes(path, sizes);
749 }
750
751 void FontClient::GetFixedSizes(const FontDescription&         fontDescription,
752                                Dali::Vector<PointSize26Dot6>& sizes)
753 {
754   GetImplementation(*this).GetFixedSizes(fontDescription, sizes);
755 }
756
757 bool FontClient::HasItalicStyle(FontId fontId) const
758 {
759   return GetImplementation(*this).HasItalicStyle(fontId);
760 }
761
762 void FontClient::GetFontMetrics(FontId fontId, FontMetrics& metrics)
763 {
764   GetImplementation(*this).GetFontMetrics(fontId, metrics);
765 }
766
767 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode)
768 {
769   return GetImplementation(*this).GetGlyphIndex(fontId, charcode);
770 }
771
772 GlyphIndex FontClient::GetGlyphIndex(FontId fontId, Character charcode, Character variantSelector)
773 {
774   return GetImplementation(*this).GetGlyphIndex(fontId, charcode, variantSelector);
775 }
776
777 bool FontClient::GetGlyphMetrics(GlyphInfo* array, uint32_t size, GlyphType type, bool horizontal)
778 {
779   return GetImplementation(*this).GetGlyphMetrics(array, size, type, horizontal);
780 }
781
782 void FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, bool isItalicRequired, bool isBoldRequired, GlyphBufferData& data, int outlineWidth)
783 {
784   GetImplementation(*this).CreateBitmap(fontId, glyphIndex, isItalicRequired, isBoldRequired, data, outlineWidth);
785 }
786
787 PixelData FontClient::CreateBitmap(FontId fontId, GlyphIndex glyphIndex, int outlineWidth)
788 {
789   return GetImplementation(*this).CreateBitmap(fontId, glyphIndex, outlineWidth);
790 }
791
792 void FontClient::CreateVectorBlob(FontId fontId, GlyphIndex glyphIndex, VectorBlob*& blob, unsigned int& blobLength, unsigned int& nominalWidth, unsigned int& nominalHeight)
793 {
794   GetImplementation(*this).CreateVectorBlob(fontId, glyphIndex, blob, blobLength, nominalWidth, nominalHeight);
795 }
796
797 const GlyphInfo& FontClient::GetEllipsisGlyph(PointSize26Dot6 requestedPointSize)
798 {
799   return GetImplementation(*this).GetEllipsisGlyph(requestedPointSize);
800 }
801
802 bool FontClient::IsColorGlyph(FontId fontId, GlyphIndex glyphIndex)
803 {
804   return GetImplementation(*this).IsColorGlyph(fontId, glyphIndex);
805 }
806
807 bool FontClient::AddCustomFontDirectory(const FontPath& path)
808 {
809   return GetImplementation(*this).AddCustomFontDirectory(path);
810 }
811
812 GlyphIndex FontClient::CreateEmbeddedItem(const EmbeddedItemDescription& description, Pixel::Format& pixelFormat)
813 {
814   return GetImplementation(*this).CreateEmbeddedItem(description, pixelFormat);
815 }
816
817 void FontClient::EnableAtlasLimitation(bool enabled)
818 {
819   return GetImplementation(*this).EnableAtlasLimitation(enabled);
820 }
821
822 bool FontClient::IsAtlasLimitationEnabled() const
823 {
824   return GetImplementation(*this).IsAtlasLimitationEnabled();
825 }
826
827 Size FontClient::GetMaximumTextAtlasSize() const
828 {
829   return GetImplementation(*this).GetMaximumTextAtlasSize();
830 }
831
832 Size FontClient::GetDefaultTextAtlasSize() const
833 {
834   return GetImplementation(*this).GetDefaultTextAtlasSize();
835 }
836
837 Size FontClient::GetCurrentMaximumBlockSizeFitInAtlas() const
838 {
839   return GetImplementation(*this).GetCurrentMaximumBlockSizeFitInAtlas();
840 }
841
842 bool FontClient::SetCurrentMaximumBlockSizeFitInAtlas(const Size& currentMaximumBlockSizeFitInAtlas)
843 {
844   return GetImplementation(*this).SetCurrentMaximumBlockSizeFitInAtlas(currentMaximumBlockSizeFitInAtlas);
845 }
846
847 uint32_t FontClient::GetNumberOfPointsPerOneUnitOfPointSize() const
848 {
849   return GetImplementation(*this).GetNumberOfPointsPerOneUnitOfPointSize();
850 }
851
852 FontClient::FontClient(Internal::FontClient* internal)
853 : BaseHandle(internal)
854 {
855 }
856
857 FontClient FontClientPreInitialize()
858 {
859   return Internal::FontClient::PreInitialize();
860 }
861
862 void FontClientPreCache(const FontFamilyList& fallbackFamilyList, const FontFamilyList& extraFamilyList, const FontFamily& localeFamily, bool useThread, bool syncCreation)
863 {
864   Internal::FontClient::PreCache(fallbackFamilyList, extraFamilyList, localeFamily, useThread, syncCreation);
865 }
866
867 void FontClientFontPreLoad(const FontPathList& fontPathList, const FontPathList& memoryFontPathList, bool useThread, bool syncCreation)
868 {
869   Internal::FontClient::PreLoad(fontPathList, memoryFontPathList, useThread, syncCreation);
870 }
871
872 void FontClientJoinFontThreads()
873 {
874   Internal::FontClient::JoinFontThreads();
875 }
876
877 } // namespace TextAbstraction
878
879 } // namespace Dali