2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/devel-api/text-abstraction/glyph-buffer-data.h>
22 #include <dali/internal/imaging/common/image-operations.h>
26 namespace TextAbstraction
28 GlyphBufferData::GlyphBufferData()
35 compressionType{CompressionType::NO_COMPRESSION},
42 GlyphBufferData::~GlyphBufferData()
50 GlyphBufferData::GlyphBufferData(GlyphBufferData&& rhs) noexcept
54 outlineOffsetX{rhs.outlineOffsetX},
55 outlineOffsetY{rhs.outlineOffsetY},
57 compressionType{rhs.compressionType},
58 isColorEmoji{rhs.isColorEmoji},
59 isColorBitmap{rhs.isColorBitmap},
60 isBufferOwned{rhs.isBufferOwned}
64 rhs.isBufferOwned = false;
67 GlyphBufferData& GlyphBufferData::operator=(GlyphBufferData&& rhs) noexcept
72 outlineOffsetX = rhs.outlineOffsetX;
73 outlineOffsetY = rhs.outlineOffsetY;
75 compressionType = rhs.compressionType;
76 isColorEmoji = rhs.isColorEmoji;
77 isColorBitmap = rhs.isColorBitmap;
78 isBufferOwned = rhs.isBufferOwned;
82 rhs.isBufferOwned = false;
87 size_t GlyphBufferData::Compress(const uint8_t* const __restrict__ inBuffer, GlyphBufferData& __restrict__ outBufferData)
89 size_t bufferSize = 0u;
90 uint8_t*& __restrict__ compressedBuffer = outBufferData.buffer;
91 switch(outBufferData.compressionType)
93 case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
95 bufferSize = static_cast<size_t>(outBufferData.width) * static_cast<size_t>(outBufferData.height) * static_cast<size_t>(Pixel::GetBytesPerPixel(outBufferData.format));
97 compressedBuffer = (uint8_t*)malloc(bufferSize);
98 if(DALI_UNLIKELY(compressedBuffer == nullptr))
102 outBufferData.isBufferOwned = true;
104 // Copy buffer without compress
105 memcpy(compressedBuffer, inBuffer, bufferSize);
108 case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
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;
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))
121 outBufferData.isBufferOwned = true;
123 uint8_t* __restrict__ outBufferPtr = compressedBuffer;
124 const uint8_t* __restrict__ inBufferPtr = inBuffer;
126 // Compress for each line
127 for(uint32_t y = 0; y < outBufferData.height; ++y)
129 for(uint32_t x = 0; x < componentCount; ++x)
131 const uint8_t v0 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
132 const uint8_t v1 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
134 *(outBufferPtr++) = (v0 << 4) | v1;
138 *(outBufferPtr++) = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
143 case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
145 const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
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))
154 uint8_t* __restrict__ outBufferPtr = tempBuffer;
155 const uint8_t* __restrict__ inBufferPtr = inBuffer;
159 // Compress for each line
160 for(uint32_t y = 0; y < outBufferData.height; ++y)
162 uint32_t encodedByte = 0;
163 while(encodedByte < widthByte)
165 // Case 1 : Remain only 1 byte
166 if(DALI_UNLIKELY(encodedByte + 1 == widthByte))
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;
174 // Case 2 : Remain only 2 byte
175 else if(DALI_UNLIKELY(encodedByte + 2 == widthByte))
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
184 *(outBufferPtr++) = 0x80 | v0;
189 *(outBufferPtr++) = 0x10 | v0;
190 *(outBufferPtr++) = v1 << 4;
194 // Case 3 : Normal case. Remain byte bigger or equal than 3.
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.
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
211 // We can compress by RLE
214 uint8_t runLength = 2;
215 while(encodedByte < widthByte && runLength < 9)
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
231 // Update (runLength - 2) result.
232 *(outBufferPtr++) = ((0x8 | (runLength - 2)) << 4) | v0;
235 // We cannot compress by RLE.
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
243 uint8_t nonRunLength = 3;
244 uint8_t* nonRunLengthHeaderPtr = outBufferPtr;
245 *(outBufferPtr++) = v0;
246 *(outBufferPtr++) = (v1 << 4) | v2;
248 while(encodedByte < widthByte && nonRunLength < 15)
250 if(DALI_LIKELY(encodedByte + 1 < widthByte))
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
258 // Stop non-compress logic.
264 *(outBufferPtr++) = (w0 << 4) | w1;
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
277 *(outBufferPtr++) = (w0 << 4);
280 // Increase nonRunLength 2 even latest value is invalid.
286 // Update (nonRunLength-1)/2 result into header.
287 *(nonRunLengthHeaderPtr) |= (nonRunLength >> 1) << 4;
293 // Allocate and copy data
294 compressedBuffer = (uint8_t*)malloc(bufferSize);
295 if(DALI_UNLIKELY(compressedBuffer == nullptr))
300 outBufferData.isBufferOwned = true;
302 memcpy(compressedBuffer, tempBuffer, bufferSize);
316 void GlyphBufferData::Decompress(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer)
318 if(DALI_UNLIKELY(outBuffer == nullptr))
323 switch(inBufferData.compressionType)
325 case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
327 const auto bufferSize = inBufferData.width * inBufferData.height * Pixel::GetBytesPerPixel(inBufferData.format);
329 // Copy buffer without compress
330 memcpy(outBuffer, inBufferData.buffer, bufferSize);
333 case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
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;
339 uint8_t* __restrict__ outBufferPtr = outBuffer;
340 const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
342 // Compress for each line
343 for(uint32_t y = 0; y < inBufferData.height; ++y)
345 for(uint32_t x = 0; x < componentCount; ++x)
347 const uint8_t v = *(inBufferPtr++);
348 const uint8_t v0 = (v >> 4) & 0x0f;
349 const uint8_t v1 = v & 0x0f;
351 *(outBufferPtr++) = (v0 << 4) | v0;
352 *(outBufferPtr++) = (v1 << 4) | v1;
356 const uint8_t v = *(inBufferPtr++);
357 *(outBufferPtr++) = (v << 4) | v;
362 case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
364 const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
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)
372 uint32_t decodedByte = 0;
373 while(decodedByte < widthByte)
375 const uint8_t v = *(inBufferPtr++);
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)
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;
390 // Not compress by RLE
393 const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
394 decodedByte += nonRunLength;
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;
400 const bool ignoreLastValue = decodedByte > widthByte ? true : false;
401 if(DALI_UNLIKELY(ignoreLastValue))
404 for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
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;
413 *(outBufferPtr++) = (w0 << 4) | w0;
414 *(outBufferPtr++) = (w1 << 4) | w1;
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;
423 *(outBufferPtr++) = (w0 << 4) | w0;
428 for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
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;
437 *(outBufferPtr++) = (w0 << 4) | w0;
438 *(outBufferPtr++) = (w1 << 4) | w1;
453 void GlyphBufferData::DecompressScanline(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer, uint32_t& __restrict__ offset)
455 switch(inBufferData.compressionType)
457 case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
459 const auto bufferSize = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
461 // Copy buffer without compress
462 memcpy(outBuffer, inBufferData.buffer + offset, bufferSize);
465 offset += bufferSize;
468 case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
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;
474 uint8_t* __restrict__ outBufferPtr = outBuffer;
475 const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
477 // Decompress scanline
478 for(uint32_t x = 0; x < componentCount; ++x)
480 const uint8_t v = *(inBufferPtr++);
481 const uint8_t v0 = (v >> 4) & 0x0f;
482 const uint8_t v1 = v & 0x0f;
484 *(outBufferPtr++) = (v0 << 4) | v0;
485 *(outBufferPtr++) = (v1 << 4) | v1;
489 const uint8_t v = *(inBufferPtr++);
490 *(outBufferPtr++) = (v << 4) | v;
494 offset += (widthByte + 1u) >> 1u;
497 case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
499 const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
501 uint8_t* __restrict__ outBufferPtr = outBuffer;
502 const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
504 // If offset is zero, fill outBuffer as 0 first.
505 if(DALI_UNLIKELY(offset == 0))
507 memset(outBufferPtr, 0, widthByte);
510 // Decompress scanline
511 uint32_t decodedByte = 0;
512 while(decodedByte < widthByte)
514 const uint8_t v = *(inBufferPtr++);
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)
524 const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
525 const uint8_t v0 = (prev0 + repeatValue) & 0x0f;
526 *(outBufferPtr++) = (v0 << 4) | v0;
529 // Not compress by RLE
532 const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
533 decodedByte += nonRunLength;
535 const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
536 const uint8_t v0 = (prev0 + (v & 0x0f)) & 0x0f;
537 *(outBufferPtr++) = (v0 << 4) | v0;
539 const bool ignoreLastValue = decodedByte > widthByte ? true : false;
540 if(DALI_UNLIKELY(ignoreLastValue))
543 for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
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;
552 *(outBufferPtr++) = (w0 << 4) | w0;
553 *(outBufferPtr++) = (w1 << 4) | w1;
557 const uint8_t w = ((*(inBufferPtr++)) >> 4) & 0x0f;
558 const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
559 const uint8_t w0 = (prew0 + w) & 0x0f;
562 *(outBufferPtr++) = (w0 << 4) | w0;
567 for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
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;
576 *(outBufferPtr++) = (w0 << 4) | w0;
577 *(outBufferPtr++) = (w1 << 4) | w1;
591 } // namespace TextAbstraction