2 * Copyright (c) 2024 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))
100 DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", bufferSize);
103 outBufferData.isBufferOwned = true;
105 // Copy buffer without compress
106 memcpy(compressedBuffer, inBuffer, bufferSize);
109 case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
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;
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))
120 DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", bufferSize);
123 outBufferData.isBufferOwned = true;
125 uint8_t* __restrict__ outBufferPtr = compressedBuffer;
126 const uint8_t* __restrict__ inBufferPtr = inBuffer;
128 // Compress for each line
129 for(uint32_t y = 0; y < outBufferData.height; ++y)
131 for(uint32_t x = 0; x < componentCount; ++x)
133 const uint8_t v0 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
134 const uint8_t v1 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
136 *(outBufferPtr++) = (v0 << 4) | v1;
140 *(outBufferPtr++) = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
145 case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
147 const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
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))
153 DALI_LOG_ERROR("malloc is failed. request malloc size : %u\n", outBufferData.height * (widthByte + 1));
157 uint8_t* __restrict__ outBufferPtr = tempBuffer;
158 const uint8_t* __restrict__ inBufferPtr = inBuffer;
162 // Compress for each line
163 for(uint32_t y = 0; y < outBufferData.height; ++y)
165 uint32_t encodedByte = 0;
166 while(encodedByte < widthByte)
168 // Case 1 : Remain only 1 byte
169 if(DALI_UNLIKELY(encodedByte + 1 == widthByte))
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;
177 // Case 2 : Remain only 2 byte
178 else if(DALI_UNLIKELY(encodedByte + 2 == widthByte))
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
187 *(outBufferPtr++) = 0x80 | v0;
192 *(outBufferPtr++) = 0x10 | v0;
193 *(outBufferPtr++) = v1 << 4;
197 // Case 3 : Normal case. Remain byte bigger or equal than 3.
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.
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
214 // We can compress by RLE
217 uint8_t runLength = 2;
218 while(encodedByte < widthByte && runLength < 9)
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
234 // Update (runLength - 2) result.
235 *(outBufferPtr++) = ((0x8 | (runLength - 2)) << 4) | v0;
238 // We cannot compress by RLE.
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
246 uint8_t nonRunLength = 3;
247 uint8_t* nonRunLengthHeaderPtr = outBufferPtr;
248 *(outBufferPtr++) = v0;
249 *(outBufferPtr++) = (v1 << 4) | v2;
251 while(encodedByte < widthByte && nonRunLength < 15)
253 if(DALI_LIKELY(encodedByte + 1 < widthByte))
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
261 // Stop non-compress logic.
267 *(outBufferPtr++) = (w0 << 4) | w1;
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
280 *(outBufferPtr++) = (w0 << 4);
283 // Increase nonRunLength 2 even latest value is invalid.
289 // Update (nonRunLength-1)/2 result into header.
290 *(nonRunLengthHeaderPtr) |= (nonRunLength >> 1) << 4;
296 // Allocate and copy data
297 compressedBuffer = (uint8_t*)malloc(bufferSize);
298 if(DALI_UNLIKELY(compressedBuffer == nullptr))
300 DALI_LOG_ERROR("malloc is failed. request malloc size : %zu\n", bufferSize);
304 outBufferData.isBufferOwned = true;
306 memcpy(compressedBuffer, tempBuffer, bufferSize);
320 void GlyphBufferData::Decompress(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer)
322 if(DALI_UNLIKELY(outBuffer == nullptr))
327 switch(inBufferData.compressionType)
329 case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
331 const auto bufferSize = inBufferData.width * inBufferData.height * Pixel::GetBytesPerPixel(inBufferData.format);
333 // Copy buffer without compress
334 memcpy(outBuffer, inBufferData.buffer, bufferSize);
337 case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
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;
343 uint8_t* __restrict__ outBufferPtr = outBuffer;
344 const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
346 // Compress for each line
347 for(uint32_t y = 0; y < inBufferData.height; ++y)
349 for(uint32_t x = 0; x < componentCount; ++x)
351 const uint8_t v = *(inBufferPtr++);
352 const uint8_t v0 = (v >> 4) & 0x0f;
353 const uint8_t v1 = v & 0x0f;
355 *(outBufferPtr++) = (v0 << 4) | v0;
356 *(outBufferPtr++) = (v1 << 4) | v1;
360 const uint8_t v = *(inBufferPtr++);
361 *(outBufferPtr++) = (v << 4) | v;
366 case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
368 const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
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)
376 uint32_t decodedByte = 0;
377 while(decodedByte < widthByte)
379 const uint8_t v = *(inBufferPtr++);
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)
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;
394 // Not compress by RLE
397 const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
398 decodedByte += nonRunLength;
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;
404 const bool ignoreLastValue = decodedByte > widthByte ? true : false;
405 if(DALI_UNLIKELY(ignoreLastValue))
408 for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
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;
417 *(outBufferPtr++) = (w0 << 4) | w0;
418 *(outBufferPtr++) = (w1 << 4) | w1;
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;
427 *(outBufferPtr++) = (w0 << 4) | w0;
432 for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
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;
441 *(outBufferPtr++) = (w0 << 4) | w0;
442 *(outBufferPtr++) = (w1 << 4) | w1;
457 void GlyphBufferData::DecompressScanline(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer, uint32_t& __restrict__ offset)
459 switch(inBufferData.compressionType)
461 case TextAbstraction::GlyphBufferData::CompressionType::NO_COMPRESSION:
463 const auto bufferSize = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
465 // Copy buffer without compress
466 memcpy(outBuffer, inBufferData.buffer + offset, bufferSize);
469 offset += bufferSize;
472 case TextAbstraction::GlyphBufferData::CompressionType::BPP_4:
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;
478 uint8_t* __restrict__ outBufferPtr = outBuffer;
479 const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
481 // Decompress scanline
482 for(uint32_t x = 0; x < componentCount; ++x)
484 const uint8_t v = *(inBufferPtr++);
485 const uint8_t v0 = (v >> 4) & 0x0f;
486 const uint8_t v1 = v & 0x0f;
488 *(outBufferPtr++) = (v0 << 4) | v0;
489 *(outBufferPtr++) = (v1 << 4) | v1;
493 const uint8_t v = *(inBufferPtr++);
494 *(outBufferPtr++) = (v << 4) | v;
498 offset += (widthByte + 1u) >> 1u;
501 case TextAbstraction::GlyphBufferData::CompressionType::RLE_4:
503 const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
505 uint8_t* __restrict__ outBufferPtr = outBuffer;
506 const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
508 // If offset is zero, fill outBuffer as 0 first.
509 if(DALI_UNLIKELY(offset == 0))
511 memset(outBufferPtr, 0, widthByte);
514 // Decompress scanline
515 uint32_t decodedByte = 0;
516 while(decodedByte < widthByte)
518 const uint8_t v = *(inBufferPtr++);
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)
528 const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
529 const uint8_t v0 = (prev0 + repeatValue) & 0x0f;
530 *(outBufferPtr++) = (v0 << 4) | v0;
533 // Not compress by RLE
536 const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
537 decodedByte += nonRunLength;
539 const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
540 const uint8_t v0 = (prev0 + (v & 0x0f)) & 0x0f;
541 *(outBufferPtr++) = (v0 << 4) | v0;
543 const bool ignoreLastValue = decodedByte > widthByte ? true : false;
544 if(DALI_UNLIKELY(ignoreLastValue))
547 for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
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;
556 *(outBufferPtr++) = (w0 << 4) | w0;
557 *(outBufferPtr++) = (w1 << 4) | w1;
561 const uint8_t w = ((*(inBufferPtr++)) >> 4) & 0x0f;
562 const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
563 const uint8_t w0 = (prew0 + w) & 0x0f;
566 *(outBufferPtr++) = (w0 << 4) | w0;
571 for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
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;
580 *(outBufferPtr++) = (w0 << 4) | w0;
581 *(outBufferPtr++) = (w1 << 4) | w1;
595 } // namespace TextAbstraction