Make GlyphBufferData as another class
[platform/core/uifw/dali-adaptor.git] / dali / devel-api / text-abstraction / glyph-buffer-data.cpp
1 /*
2  * Copyright (c) 2023 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/glyph-buffer-data.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/internal/imaging/common/image-operations.h>
23
24 namespace Dali
25 {
26 namespace TextAbstraction
27 {
28 GlyphBufferData::GlyphBufferData()
29 : buffer{nullptr},
30   width{0u},
31   height{0u},
32   outlineOffsetX{0},
33   outlineOffsetY{0},
34   format{Pixel::A8},
35   compressionType{CompressionType::NO_COMPRESSION},
36   isColorEmoji{false},
37   isColorBitmap{false},
38   isBufferOwned{false}
39 {
40 }
41
42 GlyphBufferData::~GlyphBufferData()
43 {
44   if(isBufferOwned)
45   {
46     free(buffer);
47   }
48 }
49
50 GlyphBufferData::GlyphBufferData(GlyphBufferData&& rhs) noexcept
51 : buffer{rhs.buffer},
52   width{rhs.width},
53   height{rhs.height},
54   outlineOffsetX{rhs.outlineOffsetX},
55   outlineOffsetY{rhs.outlineOffsetY},
56   format{rhs.format},
57   compressionType{rhs.compressionType},
58   isColorEmoji{rhs.isColorEmoji},
59   isColorBitmap{rhs.isColorBitmap},
60   isBufferOwned{rhs.isBufferOwned}
61 {
62   // Remove moved data
63   rhs.buffer        = nullptr;
64   rhs.isBufferOwned = false;
65 }
66
67 GlyphBufferData& GlyphBufferData::operator=(GlyphBufferData&& rhs) noexcept
68 {
69   buffer          = rhs.buffer;
70   width           = rhs.width;
71   height          = rhs.height;
72   outlineOffsetX  = rhs.outlineOffsetX;
73   outlineOffsetY  = rhs.outlineOffsetY;
74   format          = rhs.format;
75   compressionType = rhs.compressionType;
76   isColorEmoji    = rhs.isColorEmoji;
77   isColorBitmap   = rhs.isColorBitmap;
78   isBufferOwned   = rhs.isBufferOwned;
79
80   // Remove moved data
81   rhs.buffer        = nullptr;
82   rhs.isBufferOwned = false;
83
84   return *this;
85 }
86
87 size_t GlyphBufferData::Compress(const uint8_t* const __restrict__ inBuffer, GlyphBufferData& __restrict__ outBufferData)
88 {
89   size_t bufferSize                       = 0u;
90   uint8_t*& __restrict__ compressedBuffer = outBufferData.buffer;
91   switch(outBufferData.compressionType)
92   {
93     case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
94     {
95       bufferSize = static_cast<size_t>(outBufferData.width) * static_cast<size_t>(outBufferData.height) * static_cast<size_t>(Pixel::GetBytesPerPixel(outBufferData.format));
96
97       compressedBuffer = (uint8_t*)malloc(bufferSize);
98       if(DALI_UNLIKELY(compressedBuffer == nullptr))
99       {
100         return 0u;
101       }
102       outBufferData.isBufferOwned = true;
103
104       // Copy buffer without compress
105       memcpy(compressedBuffer, inBuffer, bufferSize);
106       break;
107     }
108     case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
109     {
110       const uint32_t widthByte       = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
111       const uint32_t componentCount  = (widthByte >> 1);
112       const bool     considerPadding = (widthByte & 1) ? true : false;
113
114       // For BIT_PER_PIXEL_4 type, we can know final compressed buffer size immediatly.
115       bufferSize       = static_cast<size_t>(outBufferData.height) * static_cast<size_t>(componentCount + (considerPadding ? 1 : 0));
116       compressedBuffer = (uint8_t*)malloc(bufferSize);
117       if(DALI_UNLIKELY(compressedBuffer == nullptr))
118       {
119         return 0u;
120       }
121       outBufferData.isBufferOwned = true;
122
123       uint8_t* __restrict__ outBufferPtr      = compressedBuffer;
124       const uint8_t* __restrict__ inBufferPtr = inBuffer;
125
126       // Compress for each line
127       for(uint32_t y = 0; y < outBufferData.height; ++y)
128       {
129         for(uint32_t x = 0; x < componentCount; ++x)
130         {
131           const uint8_t v0 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
132           const uint8_t v1 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
133
134           *(outBufferPtr++) = (v0 << 4) | v1;
135         }
136         if(considerPadding)
137         {
138           *(outBufferPtr++) = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
139         }
140       }
141       break;
142     }
143     case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
144     {
145       const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
146
147       // Allocate temperal buffer. Note that RLE4 can be bigger than original buffer.
148       uint8_t* __restrict__ tempBuffer = (uint8_t*)malloc(outBufferData.height * (widthByte + 1));
149       if(DALI_UNLIKELY(tempBuffer == nullptr))
150       {
151         return 0u;
152       }
153
154       uint8_t* __restrict__ outBufferPtr      = tempBuffer;
155       const uint8_t* __restrict__ inBufferPtr = inBuffer;
156
157       bufferSize = 0u;
158
159       // Compress for each line
160       for(uint32_t y = 0; y < outBufferData.height; ++y)
161       {
162         uint32_t encodedByte = 0;
163         while(encodedByte < widthByte)
164         {
165           // Case 1 : Remain only 1 byte
166           if(DALI_UNLIKELY(encodedByte + 1 == widthByte))
167           {
168             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
169             const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
170             *(outBufferPtr++)   = v0;
171             ++encodedByte;
172             ++bufferSize;
173           }
174           // Case 2 : Remain only 2 byte
175           else if(DALI_UNLIKELY(encodedByte + 2 == widthByte))
176           {
177             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
178             const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
179             const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
180             const uint8_t v1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
181             encodedByte += 2;
182             if(v0 == v1)
183             {
184               *(outBufferPtr++) = 0x80 | v0;
185               ++bufferSize;
186             }
187             else
188             {
189               *(outBufferPtr++) = 0x10 | v0;
190               *(outBufferPtr++) = v1 << 4;
191               bufferSize += 2;
192             }
193           }
194           // Case 3 : Normal case. Remain byte bigger or equal than 3.
195           else
196           {
197             // Compress rule -
198             // Read 2 byte as v0 and v1.
199             // - If v0 == v1, We can compress. mark the first bit as 1. and remain 3 bit mark as the "runLength - 2".
200             //   runLength can be maximum 9.
201             // - If v0 != v1, We cannot compress. mark the first bit as 0. and remain 3 bit mark as the "(nonRunLength - 1) / 2"
202             //   Due to the BitPerPixel is 4, nonRunLength should be odd value.
203             //   nonRunLength cutted if v0 == v1.
204             //   nonRunLength can be maximum 15.
205
206             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
207             const uint8_t v0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
208             const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
209             const uint8_t v1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
210             encodedByte += 2;
211             // We can compress by RLE
212             if(v0 == v1)
213             {
214               uint8_t runLength = 2;
215               while(encodedByte < widthByte && runLength < 9)
216               {
217                 const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
218                 const uint8_t v2    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prev2) & 0x0f; // Intented underflow
219                 if(v2 == v0)
220                 {
221                   ++inBufferPtr;
222                   ++encodedByte;
223                   ++runLength;
224                 }
225                 else
226                 {
227                   break;
228                 }
229               }
230
231               // Update (runLength - 2) result.
232               *(outBufferPtr++) = ((0x8 | (runLength - 2)) << 4) | v0;
233               ++bufferSize;
234             }
235             // We cannot compress by RLE.
236             else
237             {
238               // Read one more value.
239               const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
240               const uint8_t v2    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev2) & 0x0f; // Intented underflow
241               ++encodedByte;
242
243               uint8_t  nonRunLength          = 3;
244               uint8_t* nonRunLengthHeaderPtr = outBufferPtr;
245               *(outBufferPtr++)              = v0;
246               *(outBufferPtr++)              = (v1 << 4) | v2;
247               bufferSize += 2;
248               while(encodedByte < widthByte && nonRunLength < 15)
249               {
250                 if(DALI_LIKELY(encodedByte + 1 < widthByte))
251                 {
252                   const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
253                   const uint8_t w0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
254                   const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1 - widthByte));
255                   const uint8_t w1    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1)) - prew1) & 0x0f; // Intented underflow
256                   if(w0 == w1)
257                   {
258                     // Stop non-compress logic.
259                     break;
260                   }
261                   else
262                   {
263                     ++bufferSize;
264                     *(outBufferPtr++) = (w0 << 4) | w1;
265                     inBufferPtr += 2;
266                     encodedByte += 2;
267                     nonRunLength += 2;
268                   }
269                 }
270                 else
271                 {
272                   // Edge case. There is only one pixel remained.
273                   const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
274                   const uint8_t w0    = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
275                   {
276                     ++bufferSize;
277                     *(outBufferPtr++) = (w0 << 4);
278                     ++encodedByte;
279                     ++inBufferPtr;
280                     // Increase nonRunLength 2 even latest value is invalid.
281                     nonRunLength += 2;
282                   }
283                 }
284               }
285
286               // Update (nonRunLength-1)/2 result into header.
287               *(nonRunLengthHeaderPtr) |= (nonRunLength >> 1) << 4;
288             }
289           }
290         }
291       }
292
293       // Allocate and copy data
294       compressedBuffer = (uint8_t*)malloc(bufferSize);
295       if(DALI_UNLIKELY(compressedBuffer == nullptr))
296       {
297         free(tempBuffer);
298         return 0u;
299       }
300       outBufferData.isBufferOwned = true;
301
302       memcpy(compressedBuffer, tempBuffer, bufferSize);
303       free(tempBuffer);
304
305       break;
306     }
307     default:
308     {
309       break;
310     }
311   }
312
313   return bufferSize;
314 }
315
316 void GlyphBufferData::Decompress(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer)
317 {
318   if(DALI_UNLIKELY(outBuffer == nullptr))
319   {
320     return;
321   }
322
323   switch(inBufferData.compressionType)
324   {
325     case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
326     {
327       const auto bufferSize = inBufferData.width * inBufferData.height * Pixel::GetBytesPerPixel(inBufferData.format);
328
329       // Copy buffer without compress
330       memcpy(outBuffer, inBufferData.buffer, bufferSize);
331       break;
332     }
333     case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
334     {
335       const uint32_t widthByte       = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
336       const uint32_t componentCount  = (widthByte >> 1);
337       const bool     considerPadding = (widthByte & 1) ? true : false;
338
339       uint8_t* __restrict__ outBufferPtr      = outBuffer;
340       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
341
342       // Compress for each line
343       for(uint32_t y = 0; y < inBufferData.height; ++y)
344       {
345         for(uint32_t x = 0; x < componentCount; ++x)
346         {
347           const uint8_t v  = *(inBufferPtr++);
348           const uint8_t v0 = (v >> 4) & 0x0f;
349           const uint8_t v1 = v & 0x0f;
350
351           *(outBufferPtr++) = (v0 << 4) | v0;
352           *(outBufferPtr++) = (v1 << 4) | v1;
353         }
354         if(considerPadding)
355         {
356           const uint8_t v   = *(inBufferPtr++);
357           *(outBufferPtr++) = (v << 4) | v;
358         }
359       }
360       break;
361     }
362     case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
363     {
364       const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
365
366       uint8_t* __restrict__ outBufferPtr      = outBuffer;
367       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
368       // Compress for each line
369       for(uint32_t y = 0; y < inBufferData.height; ++y)
370       {
371         uint32_t x           = 0;
372         uint32_t decodedByte = 0;
373         while(decodedByte < widthByte)
374         {
375           const uint8_t v = *(inBufferPtr++);
376           ++x;
377           // Compress by RLE
378           if(v & 0x80)
379           {
380             const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
381             decodedByte += runLength;
382             const uint8_t repeatValue = v & 0x0f;
383             for(uint8_t iter = 0; iter < runLength; ++iter)
384             {
385               const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
386               const uint8_t v0    = (prev0 + repeatValue) & 0x0f;
387               *(outBufferPtr++)   = (v0 << 4) | v0;
388             }
389           }
390           // Not compress by RLE
391           else
392           {
393             const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
394             decodedByte += nonRunLength;
395             // First value.
396             const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
397             const uint8_t v0    = (prev0 + (v & 0x0f)) & 0x0f;
398             *(outBufferPtr++)   = (v0 << 4) | v0;
399
400             const bool ignoreLastValue = decodedByte > widthByte ? true : false;
401             if(DALI_UNLIKELY(ignoreLastValue))
402             {
403               --decodedByte;
404               for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
405               {
406                 const uint8_t w     = *(inBufferPtr++);
407                 const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
408                 const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
409                 const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
410                 const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
411                 ++x;
412
413                 *(outBufferPtr++) = (w0 << 4) | w0;
414                 *(outBufferPtr++) = (w1 << 4) | w1;
415               }
416               // Last value.
417               {
418                 const uint8_t w     = ((*(inBufferPtr++)) >> 4) & 0x0f;
419                 const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
420                 const uint8_t w0    = (prew0 + w) & 0x0f;
421                 ++x;
422
423                 *(outBufferPtr++) = (w0 << 4) | w0;
424               }
425             }
426             else
427             {
428               for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
429               {
430                 const uint8_t w     = *(inBufferPtr++);
431                 const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
432                 const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
433                 const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
434                 const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
435                 ++x;
436
437                 *(outBufferPtr++) = (w0 << 4) | w0;
438                 *(outBufferPtr++) = (w1 << 4) | w1;
439               }
440             }
441           }
442         }
443       }
444       break;
445     }
446     default:
447     {
448       break;
449     }
450   }
451 }
452
453 void GlyphBufferData::DecompressScanline(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer, uint32_t& __restrict__ offset)
454 {
455   switch(inBufferData.compressionType)
456   {
457     case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
458     {
459       const auto bufferSize = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
460
461       // Copy buffer without compress
462       memcpy(outBuffer, inBufferData.buffer + offset, bufferSize);
463
464       // Update offset
465       offset += bufferSize;
466       break;
467     }
468     case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
469     {
470       const uint32_t widthByte       = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
471       const uint32_t componentCount  = (widthByte >> 1);
472       const bool     considerPadding = (widthByte & 1) ? true : false;
473
474       uint8_t* __restrict__ outBufferPtr      = outBuffer;
475       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
476
477       // Decompress scanline
478       for(uint32_t x = 0; x < componentCount; ++x)
479       {
480         const uint8_t v  = *(inBufferPtr++);
481         const uint8_t v0 = (v >> 4) & 0x0f;
482         const uint8_t v1 = v & 0x0f;
483
484         *(outBufferPtr++) = (v0 << 4) | v0;
485         *(outBufferPtr++) = (v1 << 4) | v1;
486       }
487       if(considerPadding)
488       {
489         const uint8_t v   = *(inBufferPtr++);
490         *(outBufferPtr++) = (v << 4) | v;
491       }
492
493       // Update offset
494       offset += (widthByte + 1u) >> 1u;
495       break;
496     }
497     case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
498     {
499       const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
500
501       uint8_t* __restrict__ outBufferPtr      = outBuffer;
502       const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
503
504       // If offset is zero, fill outBuffer as 0 first.
505       if(DALI_UNLIKELY(offset == 0))
506       {
507         memset(outBufferPtr, 0, widthByte);
508       }
509
510       // Decompress scanline
511       uint32_t decodedByte = 0;
512       while(decodedByte < widthByte)
513       {
514         const uint8_t v = *(inBufferPtr++);
515         ++offset;
516         // Compress by RLE
517         if(v & 0x80)
518         {
519           const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
520           decodedByte += runLength;
521           const uint8_t repeatValue = (v & 0x0f);
522           for(uint8_t iter = 0; iter < runLength; ++iter)
523           {
524             const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
525             const uint8_t v0    = (prev0 + repeatValue) & 0x0f;
526             *(outBufferPtr++)   = (v0 << 4) | v0;
527           }
528         }
529         // Not compress by RLE
530         else
531         {
532           const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
533           decodedByte += nonRunLength;
534           // First value.
535           const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
536           const uint8_t v0    = (prev0 + (v & 0x0f)) & 0x0f;
537           *(outBufferPtr++)   = (v0 << 4) | v0;
538
539           const bool ignoreLastValue = decodedByte > widthByte ? true : false;
540           if(DALI_UNLIKELY(ignoreLastValue))
541           {
542             --decodedByte;
543             for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
544             {
545               const uint8_t w     = *(inBufferPtr++);
546               const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
547               const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
548               const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
549               const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
550               ++offset;
551
552               *(outBufferPtr++) = (w0 << 4) | w0;
553               *(outBufferPtr++) = (w1 << 4) | w1;
554             }
555             // Last value.
556             {
557               const uint8_t w     = ((*(inBufferPtr++)) >> 4) & 0x0f;
558               const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
559               const uint8_t w0    = (prew0 + w) & 0x0f;
560               ++offset;
561
562               *(outBufferPtr++) = (w0 << 4) | w0;
563             }
564           }
565           else
566           {
567             for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
568             {
569               const uint8_t w     = *(inBufferPtr++);
570               const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
571               const uint8_t w0    = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
572               const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
573               const uint8_t w1    = (prew1 + (w & 0x0f)) & 0x0f;
574               ++offset;
575
576               *(outBufferPtr++) = (w0 << 4) | w0;
577               *(outBufferPtr++) = (w1 << 4) | w1;
578             }
579           }
580         }
581       }
582       break;
583     }
584     default:
585     {
586       break;
587     }
588   }
589 }
590
591 } // namespace TextAbstraction
592
593 } // namespace Dali