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