DALI_TEST_GREATER(MAX_WIDTH_FIT_IN_ATLAS, glyphBufferData2000.width, TEST_LOCATION);
DALI_TEST_GREATER(MAX_HEIGHT_FIT_IN_ATLAS, glyphBufferData2000.height, TEST_LOCATION);
- // Release copied memories
- free(glyphBufferData200.buffer);
- free(glyphBufferData1000.buffer);
- free(glyphBufferData2000.buffer);
-
END_TEST;
}
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
NativeImageSourceQueuePtr NativeImageSourceQueue::New(Any nativeImageSourceQueue)
{
//ColorFormat will be ignored.
- NativeImageSourceQueuePtr image = new NativeImageSourceQueue(0, 0, ColorFormat::RGBA8888, nativeImageSourceQueue);
+ NativeImageSourceQueuePtr image = new NativeImageSourceQueue(0, 0, ColorFormat::BGRA8888, nativeImageSourceQueue);
if(image->mImpl)
{
return image;
#define DALI_NATIVE_IMAGE_SOURCE_QUEUE_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
public:
/**
* @brief Enumeration for the instance when creating a native image, the color depth has to be specified.
+ * @note This ColorFormat follows pixel byte order.
*/
enum class ColorFormat
{
RGB888, /// 8 red bits, 8 green bits, 8 blue bits
RGBA8888, /// 8 red bits, 8 green bits, 8 blue bits, alpha 8 bits
- RGBX8888 /// 8 red bits, 8 green bits, 8 blue bits, and 8 ignored bits
+ RGBX8888, /// 8 red bits, 8 green bits, 8 blue bits, and 8 ignored bits
+ BGR888, /// 8 blue bits, 8 green bits, 8 red bits
+ BGRA8888, /// 8 blue bits, 8 green bits, 8 red bits, alpha 8 bits
+ BGRX8888, /// 8 blue bits, 8 green bits, 8 red bits, and 8 ignored bits
};
/**
return GetImplementation(window).GetLastTouchEvent();
}
+InterceptKeyEventSignalType& InterceptKeyEventSignal(Window window)
+{
+ return GetImplementation(window).InterceptKeyEventSignal();
+}
+
} // namespace DevelWindow
} // namespace Dali
namespace DevelWindow
{
-typedef Signal<void()> EventProcessingFinishedSignalType; ///< Event Processing finished signal type
-
-typedef Signal<void(const KeyEvent&)> KeyEventSignalType; ///< Key event signal type
-
-typedef Signal<void(const TouchEvent&)> TouchEventSignalType; ///< Touch signal type
-
-typedef Signal<void(const WheelEvent&)> WheelEventSignalType; ///< Touched signal type
-
-typedef Signal<void(Window, bool)> VisibilityChangedSignalType; ///< Visibility changed signal type
-
-typedef Signal<void(Window, WindowEffectState, WindowEffectType)> TransitionEffectEventSignalType; ///< Effect signal type and state
-
-typedef Signal<void()> KeyboardRepeatSettingsChangedSignalType; ///< Keyboard repeat settings changed signal type
-
-typedef Signal<void(const std::string&, const std::string&, const Property::Array&)> AuxiliaryMessageSignalType; ///< Auxiliary message signal type
-
-typedef Signal<void(Window, bool)> AccessibilityHighlightSignalType; ///< Accessibility Highlight signal type
+typedef Signal<void()> EventProcessingFinishedSignalType; ///< Event Processing finished signal type
+typedef Signal<void(const KeyEvent&)> KeyEventSignalType; ///< Key event signal type
+typedef Signal<void(const TouchEvent&)> TouchEventSignalType; ///< Touch signal type
+typedef Signal<void(const WheelEvent&)> WheelEventSignalType; ///< Wheel signal type
+typedef Signal<void(Window, bool)> VisibilityChangedSignalType; ///< Visibility changed signal type
+typedef Signal<void(Window, WindowEffectState, WindowEffectType)> TransitionEffectEventSignalType; ///< Effect signal type and state
+typedef Signal<void()> KeyboardRepeatSettingsChangedSignalType; ///< Keyboard repeat settings changed signal type
+typedef Signal<void(const std::string&, const std::string&, const Property::Array&)> AuxiliaryMessageSignalType; ///< Auxiliary message signal type
+typedef Signal<void(Window, bool)> AccessibilityHighlightSignalType; ///< Accessibility Highlight signal type
+typedef Signal<bool(const KeyEvent&)> InterceptKeyEventSignalType; ///< Intercept Key event signal type
/**
* @brief Creates an initialized handle to a new Window.
*/
DALI_ADAPTOR_API const TouchEvent& GetLastTouchEvent(Window window);
+/**
+ * @brief The user would connect to this signal to intercept a KeyEvent at window.
+ *
+ * Intercepts KeyEvents in the window before dispatching KeyEvents to the control.
+ * If a KeyEvent is consumed, no KeyEvent is delivered to the control.
+ *
+ * @param[in] window The window instance.
+ * @return The signal to connect to
+ */
+DALI_ADAPTOR_API InterceptKeyEventSignalType& InterceptKeyEventSignal(Window window);
+
} // namespace DevelWindow
} // namespace Dali
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <dali/devel-api/text-abstraction/font-client.h>
// INTERNAL INCLUDES
+#include <dali/internal/imaging/common/image-operations.h>
#include <dali/internal/text/text-abstraction/font-client-impl.h>
namespace Dali
const uint32_t FontClient::NUMBER_OF_POINTS_PER_ONE_UNIT_OF_POINT_SIZE = 64u; //Found this value from toolkit
+// FontClient::GlyphBufferData
+
FontClient::GlyphBufferData::GlyphBufferData()
: buffer{nullptr},
width{0u},
outlineOffsetX{0},
outlineOffsetY{0},
format{Pixel::A8},
+ compressionType(CompressionType::NO_COMPRESSION),
isColorEmoji{false},
- isColorBitmap{false}
+ isColorBitmap{false},
+ isBufferOwned{false}
{
}
FontClient::GlyphBufferData::~GlyphBufferData()
{
-}
+ if(isBufferOwned)
+ {
+ free(buffer);
+ }
+}
+
+size_t FontClient::GlyphBufferData::Compress(const uint8_t* const __restrict__ inBuffer, GlyphBufferData& __restrict__ outBufferData)
+{
+ size_t bufferSize = 0u;
+ uint8_t*& __restrict__ compressedBuffer = outBufferData.buffer;
+ switch(outBufferData.compressionType)
+ {
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+ {
+ bufferSize = outBufferData.width * outBufferData.height * Pixel::GetBytesPerPixel(outBufferData.format);
+
+ compressedBuffer = (uint8_t*)malloc(bufferSize);
+ if(DALI_UNLIKELY(compressedBuffer == nullptr))
+ {
+ return 0u;
+ }
+ outBufferData.isBufferOwned = true;
+
+ // Copy buffer without compress
+ memcpy(compressedBuffer, inBuffer, bufferSize);
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+ {
+ const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
+ const uint32_t componentCount = (widthByte >> 1);
+ const bool considerPadding = (widthByte & 1) ? true : false;
+
+ // For BIT_PER_PIXEL_4 type, we can know final compressed buffer size immediatly.
+ bufferSize = outBufferData.height * (componentCount + (considerPadding ? 1 : 0));
+ compressedBuffer = (uint8_t*)malloc(bufferSize);
+ if(DALI_UNLIKELY(compressedBuffer == nullptr))
+ {
+ return 0u;
+ }
+ outBufferData.isBufferOwned = true;
+
+ uint8_t* __restrict__ outBufferPtr = compressedBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBuffer;
+
+ // Compress for each line
+ for(uint32_t y = 0; y < outBufferData.height; ++y)
+ {
+ for(uint32_t x = 0; x < componentCount; ++x)
+ {
+ const uint8_t v0 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+ const uint8_t v1 = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+
+ *(outBufferPtr++) = (v0 << 4) | v1;
+ }
+ if(considerPadding)
+ {
+ *(outBufferPtr++) = Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++));
+ }
+ }
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+ {
+ const uint32_t widthByte = outBufferData.width * Pixel::GetBytesPerPixel(outBufferData.format);
+
+ // Allocate temperal buffer. Note that RLE4 can be bigger than original buffer.
+ uint8_t* __restrict__ tempBuffer = (uint8_t*)malloc(outBufferData.height * (widthByte + 1));
+ if(DALI_UNLIKELY(tempBuffer == nullptr))
+ {
+ return 0u;
+ }
+
+ uint8_t* __restrict__ outBufferPtr = tempBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBuffer;
+
+ bufferSize = 0u;
+
+ // Compress for each line
+ for(uint32_t y = 0; y < outBufferData.height; ++y)
+ {
+ uint32_t encodedByte = 0;
+ while(encodedByte < widthByte)
+ {
+ // Case 1 : Remain only 1 byte
+ if(DALI_UNLIKELY(encodedByte + 1 == widthByte))
+ {
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+ *(outBufferPtr++) = v0;
+ ++encodedByte;
+ ++bufferSize;
+ }
+ // Case 2 : Remain only 2 byte
+ else if(DALI_UNLIKELY(encodedByte + 2 == widthByte))
+ {
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+ const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v1 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
+ encodedByte += 2;
+ if(v0 == v1)
+ {
+ *(outBufferPtr++) = 0x80 | v0;
+ ++bufferSize;
+ }
+ else
+ {
+ *(outBufferPtr++) = 0x10 | v0;
+ *(outBufferPtr++) = v1 << 4;
+ bufferSize += 2;
+ }
+ }
+ // Case 3 : Normal case. Remain byte bigger or equal than 3.
+ else
+ {
+ // Compress rule -
+ // Read 2 byte as v0 and v1.
+ // - If v0 == v1, We can compress. mark the first bit as 1. and remain 3 bit mark as the "runLength - 2".
+ // runLength can be maximum 9.
+ // - If v0 != v1, We cannot compress. mark the first bit as 0. and remain 3 bit mark as the "(nonRunLength - 1) / 2"
+ // Due to the BitPerPixel is 4, nonRunLength should be odd value.
+ // nonRunLength cutted if v0 == v1.
+ // nonRunLength can be maximum 15.
+
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev0) & 0x0f; // Intented underflow
+ const uint8_t prev1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v1 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev1) & 0x0f; // Intented underflow
+ encodedByte += 2;
+ // We can compress by RLE
+ if(v0 == v1)
+ {
+ uint8_t runLength = 2;
+ while(encodedByte < widthByte && runLength < 9)
+ {
+ const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v2 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prev2) & 0x0f; // Intented underflow
+ if(v2 == v0)
+ {
+ ++inBufferPtr;
+ ++encodedByte;
+ ++runLength;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // Update (runLength - 2) result.
+ *(outBufferPtr++) = ((0x8 | (runLength - 2)) << 4) | v0;
+ ++bufferSize;
+ }
+ // We cannot compress by RLE.
+ else
+ {
+ // Read one more value.
+ const uint8_t prev2 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t v2 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr++)) - prev2) & 0x0f; // Intented underflow
+ ++encodedByte;
+
+ uint8_t nonRunLength = 3;
+ uint8_t* nonRunLengthHeaderPtr = outBufferPtr;
+ *(outBufferPtr++) = v0;
+ *(outBufferPtr++) = (v1 << 4) | v2;
+ bufferSize += 2;
+ while(encodedByte < widthByte && nonRunLength < 15)
+ {
+ if(DALI_LIKELY(encodedByte + 1 < widthByte))
+ {
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t w0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
+ const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1 - widthByte));
+ const uint8_t w1 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr + 1)) - prew1) & 0x0f; // Intented underflow
+ if(w0 == w1)
+ {
+ // Stop non-compress logic.
+ break;
+ }
+ else
+ {
+ ++bufferSize;
+ *(outBufferPtr++) = (w0 << 4) | w1;
+ inBufferPtr += 2;
+ encodedByte += 2;
+ nonRunLength += 2;
+ }
+ }
+ else
+ {
+ // Edge case. There is only one pixel remained.
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr - widthByte));
+ const uint8_t w0 = (Dali::Internal::Platform::CompressBitPerPixel8To4(*(inBufferPtr)) - prew0) & 0x0f; // Intented underflow
+ {
+ ++bufferSize;
+ *(outBufferPtr++) = (w0 << 4);
+ ++encodedByte;
+ ++inBufferPtr;
+ // Increase nonRunLength 2 even latest value is invalid.
+ nonRunLength += 2;
+ }
+ }
+ }
+
+ // Update (nonRunLength-1)/2 result into header.
+ *(nonRunLengthHeaderPtr) |= (nonRunLength >> 1) << 4;
+ }
+ }
+ }
+ }
+
+ // Allocate and copy data
+ compressedBuffer = (uint8_t*)malloc(bufferSize);
+ if(DALI_UNLIKELY(compressedBuffer == nullptr))
+ {
+ free(tempBuffer);
+ return 0u;
+ }
+ outBufferData.isBufferOwned = true;
+
+ memcpy(compressedBuffer, tempBuffer, bufferSize);
+ free(tempBuffer);
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return bufferSize;
+}
+
+void FontClient::GlyphBufferData::Decompress(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer)
+{
+ if(DALI_UNLIKELY(outBuffer == nullptr))
+ {
+ return;
+ }
+
+ switch(inBufferData.compressionType)
+ {
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+ {
+ const auto bufferSize = inBufferData.width * inBufferData.height * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ // Copy buffer without compress
+ memcpy(outBuffer, inBufferData.buffer, bufferSize);
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+ const uint32_t componentCount = (widthByte >> 1);
+ const bool considerPadding = (widthByte & 1) ? true : false;
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
+
+ // Compress for each line
+ for(uint32_t y = 0; y < inBufferData.height; ++y)
+ {
+ for(uint32_t x = 0; x < componentCount; ++x)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ const uint8_t v0 = (v >> 4) & 0x0f;
+ const uint8_t v1 = v & 0x0f;
+
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ *(outBufferPtr++) = (v1 << 4) | v1;
+ }
+ if(considerPadding)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ *(outBufferPtr++) = (v << 4) | v;
+ }
+ }
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer;
+ // Compress for each line
+ for(uint32_t y = 0; y < inBufferData.height; ++y)
+ {
+ uint32_t x = 0;
+ uint32_t decodedByte = 0;
+ while(decodedByte < widthByte)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ ++x;
+ // Compress by RLE
+ if(v & 0x80)
+ {
+ const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
+ decodedByte += runLength;
+ const uint8_t repeatValue = v & 0x0f;
+ for(uint8_t iter = 0; iter < runLength; ++iter)
+ {
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t v0 = (prev0 + repeatValue) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ }
+ }
+ // Not compress by RLE
+ else
+ {
+ const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
+ decodedByte += nonRunLength;
+ // First value.
+ const uint8_t prev0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t v0 = (prev0 + (v & 0x0f)) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+
+ const bool ignoreLastValue = decodedByte > widthByte ? true : false;
+ if(DALI_UNLIKELY(ignoreLastValue))
+ {
+ --decodedByte;
+ for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++x;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ // Last value.
+ {
+ const uint8_t w = ((*(inBufferPtr++)) >> 4) & 0x0f;
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t w0 = (prew0 + w) & 0x0f;
+ ++x;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ }
+ }
+ else
+ {
+ for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = DALI_UNLIKELY(y == 0) ? 0 : (*(outBufferPtr - widthByte + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++x;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void FontClient::GlyphBufferData::DecompressScanline(const GlyphBufferData& __restrict__ inBufferData, uint8_t* __restrict__ outBuffer, uint32_t& __restrict__ offset)
+{
+ switch(inBufferData.compressionType)
+ {
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION:
+ {
+ const auto bufferSize = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ // Copy buffer without compress
+ memcpy(outBuffer, inBufferData.buffer + offset, bufferSize);
+
+ // Update offset
+ offset += bufferSize;
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+ const uint32_t componentCount = (widthByte >> 1);
+ const bool considerPadding = (widthByte & 1) ? true : false;
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
+
+ // Decompress scanline
+ for(uint32_t x = 0; x < componentCount; ++x)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ const uint8_t v0 = (v >> 4) & 0x0f;
+ const uint8_t v1 = v & 0x0f;
+
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ *(outBufferPtr++) = (v1 << 4) | v1;
+ }
+ if(considerPadding)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ *(outBufferPtr++) = (v << 4) | v;
+ }
+
+ // Update offset
+ offset += (widthByte + 1u) >> 1u;
+ break;
+ }
+ case TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4:
+ {
+ const uint32_t widthByte = inBufferData.width * Pixel::GetBytesPerPixel(inBufferData.format);
+
+ uint8_t* __restrict__ outBufferPtr = outBuffer;
+ const uint8_t* __restrict__ inBufferPtr = inBufferData.buffer + offset;
+
+ // If offset is zero, fill outBuffer as 0 first.
+ if(DALI_UNLIKELY(offset == 0))
+ {
+ memset(outBufferPtr, 0, widthByte);
+ }
+
+ // Decompress scanline
+ uint32_t decodedByte = 0;
+ while(decodedByte < widthByte)
+ {
+ const uint8_t v = *(inBufferPtr++);
+ ++offset;
+ // Compress by RLE
+ if(v & 0x80)
+ {
+ const uint8_t runLength = ((v >> 4) & 0x07) + 2u;
+ decodedByte += runLength;
+ const uint8_t repeatValue = (v & 0x0f);
+ for(uint8_t iter = 0; iter < runLength; ++iter)
+ {
+ const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t v0 = (prev0 + repeatValue) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+ }
+ }
+ // Not compress by RLE
+ else
+ {
+ const uint8_t nonRunLength = (((v >> 4) & 0x07) << 1u) + 1u;
+ decodedByte += nonRunLength;
+ // First value.
+ const uint8_t prev0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t v0 = (prev0 + (v & 0x0f)) & 0x0f;
+ *(outBufferPtr++) = (v0 << 4) | v0;
+
+ const bool ignoreLastValue = decodedByte > widthByte ? true : false;
+ if(DALI_UNLIKELY(ignoreLastValue))
+ {
+ --decodedByte;
+ for(uint8_t iter = 1; iter + 2 < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++offset;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ // Last value.
+ {
+ const uint8_t w = ((*(inBufferPtr++)) >> 4) & 0x0f;
+ const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t w0 = (prew0 + w) & 0x0f;
+ ++offset;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ }
+ }
+ else
+ {
+ for(uint8_t iter = 1; iter < nonRunLength; iter += 2)
+ {
+ const uint8_t w = *(inBufferPtr++);
+ const uint8_t prew0 = (*(outBufferPtr)) & 0x0f;
+ const uint8_t w0 = (prew0 + ((w >> 4) & 0x0f)) & 0x0f;
+ const uint8_t prew1 = (*(outBufferPtr + 1)) & 0x0f;
+ const uint8_t w1 = (prew1 + (w & 0x0f)) & 0x0f;
+ ++offset;
+
+ *(outBufferPtr++) = (w0 << 4) | w0;
+ *(outBufferPtr++) = (w1 << 4) | w1;
+ }
+ }
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+// FontClient
FontClient FontClient::Get()
{
#define DALI_PLATFORM_TEXT_ABSTRACTION_FONT_CLIENT_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*/
~GlyphBufferData();
- unsigned char* buffer; ///< The glyph's bitmap buffer data.
- unsigned int width; ///< The width of the bitmap.
- unsigned int height; ///< The height of the bitmap.
- int outlineOffsetX; ///< The additional horizontal offset to be added for the glyph's position for outline.
- int outlineOffsetY; ///< The additional vertical offset to be added for the glyph's position for outline.
- Pixel::Format format; ///< The pixel's format of the bitmap.
- bool isColorEmoji : 1; ///< Whether the glyph is an emoji.
- bool isColorBitmap : 1; ///< Whether the glyph is a color bitmap.
+ // Compression method of buffer. Each buffer compressed line by line
+ enum class CompressionType
+ {
+ NO_COMPRESSION = 0, // No compression
+ BPP_4 = 1, // Compress as 4 bit. Color become value * 17 (0x00, 0x11, 0x22, ... 0xee, 0xff).
+ // Only works for Pixel::L8 format
+ RLE_4 = 2, // Compress as 4 bit, and Run-Length-Encode. For more high compress rate, we store difference between previous scanline.
+ // Only works for Pixel::L8 format
+ };
+
+ /**
+ * @brief Helper static function to compress raw buffer from inBuffer to outBufferData.buffer.
+ * outBufferData will have it's own buffer.
+ *
+ * @pre outBufferData must not have it's own buffer.
+ * @param[in] inBuffer The input raw data.
+ * @param[in, out] outBufferData The output glyph buffer data.
+ * @return Size of compressed out buffer, Or 0 if compress failed.
+ */
+ static size_t Compress(const uint8_t* const inBuffer, GlyphBufferData& outBufferData);
+
+ /**
+ * @brief Helper static function to decompress raw buffer from inBuffer to outBufferPtr.
+ * If outBuffer is nullptr, Do nothing.
+ *
+ * @pre outBuffer memory should be allocated.
+ * @param[in] inBufferData The input glyph buffer data.
+ * @param[in, out] outBuffer The output pointer of raw buffer data.
+ */
+ static void Decompress(const GlyphBufferData& inBufferData, uint8_t* outBuffer);
+
+ /**
+ * @brief Special Helper static function to decompress raw buffer from inBuffer to outBuffer one scanline.
+ * After decompress one scanline successed, offset will be changed.
+ *
+ * @pre outBuffer memory should be allocated.
+ * @pre if inBufferData's compression type is RLE4, outBuffer memory should store the previous scanline data.
+ * @param[in] inBufferData The input glyph buffer data.
+ * @param[in, out] outBuffer The output pointer of raw buffer data.
+ * @param[in, out] offset The offset of input. It will be changed as next scanline's offset.
+ */
+ static void DecompressScanline(const GlyphBufferData& inBufferData, uint8_t* outBuffer, uint32_t& offset);
+
+ uint8_t* buffer; ///< The glyph's bitmap buffer data.
+ uint32_t width; ///< The width of the bitmap.
+ uint32_t height; ///< The height of the bitmap.
+ int outlineOffsetX; ///< The additional horizontal offset to be added for the glyph's position for outline.
+ int outlineOffsetY; ///< The additional vertical offset to be added for the glyph's position for outline.
+ Pixel::Format format; ///< The pixel's format of the bitmap.
+ CompressionType compressionType : 3; ///< The type of buffer compression.
+ bool isColorEmoji : 1; ///< Whether the glyph is an emoji.
+ bool isColorBitmap : 1; ///< Whether the glyph is a color bitmap.
+ bool isBufferOwned : 1; ///< Whether the glyph's bitmap buffer data owned by this class or not. Becareful when you use non-owned buffer data.
};
/**
}
/**
+ * @copydoc Dali::Integration::SceneHolder::InterceptKeyEventSignal()
+ */
+ Dali::Integration::SceneHolder::KeyEventGeneratedSignalType& InterceptKeyEventSignal()
+ {
+ return mScene.InterceptKeyEventSignal();
+ }
+
+ /**
* @copydoc Dali::Integration::SceneHolder::TouchedSignal()
*/
Dali::Integration::SceneHolder::TouchEventSignalType& TouchedSignal()
return GetImplementation(*this).KeyEventGeneratedSignal();
}
+SceneHolder::KeyEventGeneratedSignalType& SceneHolder::InterceptKeyEventSignal()
+{
+ return GetImplementation(*this).InterceptKeyEventSignal();
+}
+
SceneHolder::TouchEventSignalType& SceneHolder::TouchedSignal()
{
return GetImplementation(*this).TouchedSignal();
KeyEventGeneratedSignalType& KeyEventGeneratedSignal();
/**
+ * @brief This signal is emitted when key event is received.
+ * Intercepts KeyEvents in the window before dispatching KeyEvents to the control.
+ * If a KeyEvent is consumed, no KeyEvent is delivered to the control.
+ *
+ * A callback of the following type may be connected:
+ * @code
+ * bool YourCallbackName(const KeyEvent& event);
+ * @endcode
+ * @return The signal to connect to
+ */
+ KeyEventGeneratedSignalType& InterceptKeyEventSignal();
+
+ /**
* @brief This signal is emitted when the screen is touched and when the touch ends
* (i.e. the down & up touch events only).
*
LinearSampleGeneric<Pixel4Bytes, BilinearFilter4Bytes, true>(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions);
}
+// Dispatch to a format-appropriate linear sampling function:
+void LinearSample(const unsigned char* __restrict__ inPixels,
+ ImageDimensions inDimensions,
+ unsigned int inStride,
+ Pixel::Format pixelFormat,
+ unsigned char* __restrict__ outPixels,
+ ImageDimensions outDimensions)
+{
+ // Check the pixel format is one that is supported:
+ if(pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565)
+ {
+ switch(pixelFormat)
+ {
+ case Pixel::RGB888:
+ {
+ LinearSample3BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::RGBA8888:
+ {
+ LinearSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::L8:
+ case Pixel::A8:
+ {
+ LinearSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::LA88:
+ {
+ LinearSample2BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ case Pixel::RGB565:
+ {
+ LinearSampleRGB565(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ break;
+ }
+ default:
+ {
+ DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+ }
+ }
+ }
+ else
+ {
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not linear sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
+ }
+}
+
void Resample(const unsigned char* __restrict__ inPixels,
ImageDimensions inputDimensions,
unsigned int inputStride,
Resample(inPixels, inputDimensions, inputStride, outPixels, desiredDimensions, Resampler::LANCZOS4, 1, false);
}
-// Dispatch to a format-appropriate linear sampling function:
-void LinearSample(const unsigned char* __restrict__ inPixels,
- ImageDimensions inDimensions,
- unsigned int inStride,
- Pixel::Format pixelFormat,
- unsigned char* __restrict__ outPixels,
- ImageDimensions outDimensions)
+// Dispatch to a format-appropriate third-party resampling function:
+void LanczosSample(const unsigned char* __restrict__ inPixels,
+ ImageDimensions inDimensions,
+ unsigned int inStride,
+ Pixel::Format pixelFormat,
+ unsigned char* __restrict__ outPixels,
+ ImageDimensions outDimensions)
{
// Check the pixel format is one that is supported:
- if(pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565)
+ if(pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::BGRA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
{
switch(pixelFormat)
{
- case Pixel::RGB888:
- {
- LinearSample3BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
- break;
- }
case Pixel::RGBA8888:
+ case Pixel::BGRA8888:
{
- LinearSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ LanczosSample4BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
break;
}
case Pixel::L8:
case Pixel::A8:
{
- LinearSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
- break;
- }
- case Pixel::LA88:
- {
- LinearSample2BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
- break;
- }
- case Pixel::RGB565:
- {
- LinearSampleRGB565(inPixels, inDimensions, inStride, outPixels, outDimensions);
+ LanczosSample1BPP(inPixels, inDimensions, inStride, outPixels, outDimensions);
break;
}
default:
}
else
{
- DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not linear sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
+ DALI_LOG_INFO(gImageOpsLogFilter, Dali::Integration::Log::Verbose, "Bitmap was not lanczos sampled: unsupported pixel format: %u.\n", unsigned(pixelFormat));
}
}
ImageDimensions desiredDimensions);
/**
+ * @brief Resample input image to output image using a Lanczos algorithm.
+ *
+ * @pre @p inPixels must not alias @p outPixels. The input image should be a totally
+ * separate buffer from the output buffer.
+ *
+ * @param[in] inPixels Pointer to the input image buffer.
+ * @param[in] inputDimensions The input dimensions of the image.
+ * @param[in] inputStride The input stride of the image.
+ * @param[in] pixelFormat The format of the image pointed at by pixels.
+ * @param[out] outPixels Pointer to the output image buffer.
+ * @param[in] desiredDimensions The output dimensions of the image.
+ */
+void LanczosSample(const unsigned char* __restrict__ inPixels,
+ ImageDimensions inDimensions,
+ unsigned int inStride,
+ Pixel::Format pixelFormat,
+ unsigned char* __restrict__ outPixels,
+ ImageDimensions outDimensions);
+
+/**
* @brief Resamples the input image with the Lanczos algorithm.
*
* @pre @p inPixels must not alias @p outPixels. The input image should be a totally
* @param y The value between [0..255]
* @return (x*y)/255
*/
-inline uint8_t MultiplyAndNormalizeColor(const uint8_t& x, const uint8_t& y) noexcept
+inline uint8_t MultiplyAndNormalizeColor(const uint8_t x, const uint8_t y) noexcept
{
const uint32_t xy = static_cast<const uint32_t>(x) * y;
return ((xy << 15) + (xy << 7) + xy) >> 23;
}
+/**
+ * @brief Fast division by 17 and roundup. It will be useful when we compress 8bit luminance value as 4bit for text glyph.
+ *
+ * @param x The value between [0..255]
+ * @return round(x / 17.0f).(same as (x+8)/17)
+ */
+inline uint8_t CompressBitPerPixel8To4(const uint8_t x) noexcept
+{
+ return ((((static_cast<const uint16_t>(x) << 4) - x + (x >> 4)) >> 7) + 1) >> 1;
+}
+
/**@}*/
} /* namespace Platform */
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 Samsung Electronics Co., Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
{
#define TBM_SURFACE_QUEUE_SIZE 3
-const char* SAMPLER_TYPE = "samplerExternalOES";
+const char* SAMPLER_TYPE = "samplerExternalOES";
// clang-format off
int FORMATS_BLENDING_REQUIRED[] = {
switch(colorFormat)
{
- case Dali::NativeImageSourceQueue::ColorFormat::RGBA8888:
+ case Dali::NativeImageSourceQueue::ColorFormat::RGBA8888: // TODO : Implement me after other codes fixed.
+ case Dali::NativeImageSourceQueue::ColorFormat::BGRA8888:
{
tbmFormat = TBM_FORMAT_ARGB8888;
mBlendingRequired = true;
break;
}
- case Dali::NativeImageSourceQueue::ColorFormat::RGBX8888:
+ case Dali::NativeImageSourceQueue::ColorFormat::RGBX8888: // TODO : Implement me after other codes fixed.
+ case Dali::NativeImageSourceQueue::ColorFormat::BGRX8888:
{
tbmFormat = TBM_FORMAT_XRGB8888;
mBlendingRequired = false;
break;
}
- case Dali::NativeImageSourceQueue::ColorFormat::RGB888:
+ case Dali::NativeImageSourceQueue::ColorFormat::RGB888: // TODO : Implement me after other codes fixed.
+ case Dali::NativeImageSourceQueue::ColorFormat::BGR888:
{
tbmFormat = TBM_FORMAT_RGB888;
mBlendingRequired = false;
// Retrieve the image
TextAbstraction::FontClient::GlyphBufferData data;
- std::unique_ptr<GlyphBuffer> glyphBufferPtr(new GlyphBuffer(data, GlyphBuffer::FREE));
if(isEmoji)
{
data.width = parameters.glyphs[run.glyphIndex].width;
unsigned int heightOut = data.height;
const unsigned int pixelSize = Pixel::GetBytesPerPixel(data.format);
+ // If we need to decompress, create new memory and replace ownership.
+ if(data.compressionType != TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION)
+ {
+ uint8_t* newBuffer = (uint8_t*)malloc(widthOut * heightOut * pixelSize);
+ if(DALI_LIKELY(newBuffer != nullptr))
+ {
+ TextAbstraction::FontClient::GlyphBufferData::Decompress(data, newBuffer);
+ if(data.isBufferOwned)
+ {
+ // Release previous buffer
+ free(data.buffer);
+ }
+ data.isBufferOwned = true;
+ data.buffer = newBuffer;
+ data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ }
+ }
+
Dali::Internal::Platform::RotateByShear(data.buffer,
data.width,
data.height,
heightOut);
if(nullptr != pixelsOut)
{
- free(data.buffer);
- data.buffer = pixelsOut;
- data.width = widthOut;
- data.height = heightOut;
+ if(data.isBufferOwned)
+ {
+ free(data.buffer);
+ }
+ data.isBufferOwned = true;
+ data.compressionType = Dali::TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ data.buffer = pixelsOut;
+ data.width = widthOut;
+ data.height = heightOut;
}
glyphX = centerX - 0.5 * static_cast<double>(data.width);
data.isColorBitmap = font.isColorFont;
- ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer());
-
- // Sets the pixel format.
- data.format = pixelBuffer.GetPixelFormat();
+ ConvertBitmap(data, data.width, data.height, pixelBuffer.GetBuffer(), pixelBuffer.GetPixelFormat());
break;
}
++index;
Devel::PixelBuffer pixelBuffer = pixelBufferCache[pixelBufferId - 1u].pixelBuffer;
if(pixelBuffer)
{
- ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer());
-
- // Sets the pixel format.
- data.format = pixelBuffer.GetPixelFormat();
+ ConvertBitmap(data, pixelBuffer.GetWidth(), pixelBuffer.GetHeight(), pixelBuffer.GetBuffer(), pixelBuffer.GetPixelFormat());
}
}
else
{
+ data.isBufferOwned = true;
+ data.compressionType = Dali::TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
// Creates the output buffer
- const unsigned int bufferSize = data.width * data.height * 4u;
- data.buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
+ const uint32_t bufferSize = data.width * data.height * 4u;
+ data.buffer = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
memset(data.buffer, 0u, bufferSize);
* @param[in] srcHeight The height of the bitmap.
* @param[in] srcBuffer The buffer of the bitmap.
*/
-void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer)
+void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, unsigned int srcWidth, unsigned int srcHeight, const unsigned char* const srcBuffer, const Pixel::Format srcFormat)
{
// Set the input dimensions.
const ImageDimensions inputDimensions(srcWidth, srcHeight);
data.height = (data.height == 0) ? srcHeight : data.height;
const ImageDimensions desiredDimensions(data.width, data.height);
+ data.format = srcFormat;
+
+ // Note we don't compress here
+ data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
+ const uint32_t bytePerPixel = Dali::Pixel::GetBytesPerPixel(srcFormat);
+
// Creates the output buffer
- const unsigned int bufferSize = data.width * data.height * 4u;
- data.buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
+ const uint32_t bufferSize = data.width * data.height * bytePerPixel;
if(inputDimensions == desiredDimensions)
{
// There isn't downscaling.
- memcpy(data.buffer, srcBuffer, bufferSize);
+ data.isBufferOwned = false;
+ data.buffer = const_cast<uint8_t*>(srcBuffer);
}
else
{
- Dali::Internal::Platform::LanczosSample4BPP(srcBuffer,
- inputDimensions,
- srcWidth,
- data.buffer,
- desiredDimensions);
+ data.isBufferOwned = true;
+ data.buffer = (uint8_t*)malloc(bufferSize); // @note The caller is responsible for deallocating the bitmap data using free.
+ Dali::Internal::Platform::LanczosSample(srcBuffer,
+ inputDimensions,
+ srcWidth,
+ srcFormat,
+ data.buffer,
+ desiredDimensions);
}
}
*/
void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data, FT_Bitmap& srcBitmap, bool isShearRequired, bool moveBuffer)
{
+ data.buffer = nullptr;
if(srcBitmap.width * srcBitmap.rows > 0)
{
switch(srcBitmap.pixel_mode)
data.height = height;
data.format = Pixel::L8; // Sets the pixel format.
+ // Note we don't compress here
+ data.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+
if(moveBuffer)
{
- data.buffer = pixelsIn;
+ data.isBufferOwned = true;
+ data.buffer = pixelsIn;
// Happy trick for copyless convert bitmap!
srcBitmap.buffer = nullptr;
}
else
{
- const unsigned int bufferSize = width * height;
- data.buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
-
- memcpy(data.buffer, pixelsIn, bufferSize);
+ data.isBufferOwned = false;
+ data.buffer = pixelsIn;
}
if(releaseRequiredPixelPtr)
if(srcBitmap.pitch == static_cast<int>(srcBitmap.width << 2u))
{
// Color glyph doesn't support copyless convert bitmap. Just memcpy
- ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer);
-
- // Sets the pixel format.
- data.format = Pixel::BGRA8888;
+ ConvertBitmap(data, srcBitmap.width, srcBitmap.rows, srcBitmap.buffer, Pixel::BGRA8888);
}
break;
}
void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data,
unsigned int srcWidth,
unsigned int srcHeight,
- const unsigned char* const srcBuffer);
+ const unsigned char* const srcBuffer,
+ const Pixel::Format srcFormat);
void ConvertBitmap(TextAbstraction::FontClient::GlyphBufferData& data,
FT_Bitmap& srcBitmap,
* @brief Maximum size of glyph cache per each font face.
*/
constexpr std::size_t DEFAULT_GLYPH_CACHE_MAX = 128;
-constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 2u;
+constexpr std::size_t MINIMUM_SIZE_OF_GLYPH_CACHE_MAX = 3u;
constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
* @note This value fixed when we call it first time.
* @return The max size of glyph cache.
*/
-size_t GetMaxNumberOfGlyphCache()
+inline const size_t GetMaxNumberOfGlyphCache()
{
using Dali::EnvironmentVariable::GetEnvironmentVariable;
static auto numberString = GetEnvironmentVariable(MAX_NUMBER_OF_GLYPH_CACHE_ENV);
static auto number = numberString ? std::strtoul(numberString, nullptr, 10) : DEFAULT_GLYPH_CACHE_MAX;
return (number < MINIMUM_SIZE_OF_GLYPH_CACHE_MAX) ? MINIMUM_SIZE_OF_GLYPH_CACHE_MAX : number;
}
+
+/**
+ * @brief Behavior about cache the rendered glyph cache.
+ */
+constexpr bool DEFAULT_ENABLE_CACHE_RENDERED_GLYPH = true;
+constexpr auto ENABLE_CACHE_RENDERED_GLYPH_ENV = "DALI_ENABLE_CACHE_RENDERED_GLYPH";
+
+/**
+ * @brief Get whether we allow to cache rendered glyph from environment.
+ * If not settuped, default as true.
+ * @note This value fixed when we call it first time.
+ * @return True if we allow to cache rendered glyph.
+ */
+inline const bool EnableCacheRenderedGlyph()
+{
+ using Dali::EnvironmentVariable::GetEnvironmentVariable;
+ static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV);
+ static auto number = numberString ? (std::strtoul(numberString, nullptr, 10) ? true : false) : DEFAULT_ENABLE_CACHE_RENDERED_GLYPH;
+ return number;
+}
+
+/**
+ * @brief Policy about compress the cached rendered glyph.
+ * It will be used only if CacheRenderedGlyph is enabled
+ */
+constexpr auto DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY =
+#if !(defined(DALI_PROFILE_UBUNTU) || defined(ANDROID) || defined(WIN32) || defined(__APPLE__))
+ GlyphCacheManager::CompressionPolicyType::MEMORY; // If tizen target
+#else
+ GlyphCacheManager::CompressionPolicyType::SPEED; // If not tizen target
+#endif
+constexpr auto RENDERED_GLYPH_COMPRESS_POLICY_ENV = "DALI_RENDERED_GLYPH_COMPRESS_POLICY";
+
+/**
+ * @brief Get whether we allow to cache rendered glyph from environment.
+ * If not settuped, default value used, as defined above.
+ * @note This value fixed when we call it first time.
+ * @return SPEED if value start with 's' or 'S'. MEMORY if value start with 'm' or 'M'. otherwise, use default
+ */
+inline const GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
+{
+ using Dali::EnvironmentVariable::GetEnvironmentVariable;
+ static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV);
+
+ static auto policy = policyString ? policyString[0] == 's' || policyString[0] == 'S' ? GlyphCacheManager::CompressionPolicyType::SPEED
+ : policyString[0] == 'm' || policyString[0] == 'M' ? GlyphCacheManager::CompressionPolicyType::MEMORY
+ : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY
+ : DEFAULT_RENDERED_GLYPH_COMPRESS_POLICY;
+ return policy;
+}
} // namespace
FontFaceCacheItem::FontFaceCacheItem(FT_Library& freeTypeLibrary,
* @brief Create a bitmap representation of a glyph from a face font
*
* @param[in] glyphIndex The index of a glyph within the specified font.
- * @param[in] isItalicRequired Whether the glyph requires italic style.
- * @param[in] isBoldRequired Whether the glyph requires bold style.
* @param[out] data The bitmap data.
* @param[in] outlineWidth The width of the glyph outline in pixels.
+ * @param[in] isItalicRequired Whether the glyph requires italic style.
+ * @param[in] isBoldRequired Whether the glyph requires bold style.
*/
void FontFaceCacheItem::CreateBitmap(
GlyphIndex glyphIndex, Dali::TextAbstraction::FontClient::GlyphBufferData& data, int outlineWidth, bool isItalicRequired, bool isBoldRequired) const
{
GlyphCacheManager::GlyphCacheData glyphData;
FT_Error error;
+ FT_Int32 loadFlag;
// For the software italics.
bool isShearRequired = false;
// Check to see if this is fixed size bitmap
if(mIsFixedSizeBitmap)
{
- mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, isBoldRequired, glyphData, error);
+ loadFlag = FT_LOAD_COLOR;
}
else
#endif
// FT_LOAD_DEFAULT causes some issues in the alignment of the glyph inside the bitmap.
// i.e. with the SNum-3R font.
// @todo: add an option to use the FT_LOAD_DEFAULT if required?
- mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_NO_AUTOHINT, isBoldRequired, glyphData, error);
+ loadFlag = FT_LOAD_NO_AUTOHINT;
}
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, glyphData, error);
+
if(FT_Err_Ok == error)
{
if(isItalicRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_ITALIC))
isShearRequired = true;
}
- // Convert to bitmap if necessary
if(!glyphData.mIsBitmap)
{
+ // Convert to bitmap if necessary
FT_Glyph glyph = glyphData.mGlyph;
DALI_ASSERT_ALWAYS(glyph->format != FT_GLYPH_FORMAT_BITMAP && "Something wrong with cashing. Some bitmap glyph cached failed.");
}
}
- // Copy new glyph, and keep original cached glyph.
- // If we already copy new glyph by stroke, just re-use that.
- error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
- if(FT_Err_Ok == error)
- {
- FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
-
- if(isOutlineGlyph)
- {
- // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
- data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
- data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
- }
-
- // Move bitmap buffer into data.buffer
- ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+ const bool ableUseCachedRenderedGlyph = EnableCacheRenderedGlyph() && !isOutlineGlyph && !isShearRequired;
- // Copied FT_Glyph object must be released with FT_Done_Glyph
- FT_Done_Glyph(glyph);
+ // If we cache rendered glyph, and if we can use it, use cached thing first.
+ if(ableUseCachedRenderedGlyph && glyphData.mRenderedBuffer)
+ {
+ data.buffer = glyphData.mRenderedBuffer->buffer;
+ data.width = glyphData.mRenderedBuffer->width;
+ data.height = glyphData.mRenderedBuffer->height;
+ data.format = glyphData.mRenderedBuffer->format;
+ data.compressionType = glyphData.mRenderedBuffer->compressionType;
+ data.isBufferOwned = false;
}
else
{
- DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
+ // Copy new glyph, and keep original cached glyph.
+ // If we already copy new glyph by stroke, just re-use that.
+ error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, isStrokeGlyphSuccess);
+ if(FT_Err_Ok == error)
+ {
+ FT_BitmapGlyph bitmapGlyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
+
+ if(isOutlineGlyph)
+ {
+ // Calculate the additional horizontal and vertical offsets needed for the position of the outline glyph
+ data.outlineOffsetX = offsetX - bitmapGlyph->left - outlineWidth;
+ data.outlineOffsetY = bitmapGlyph->top - offsetY - outlineWidth;
+ }
+
+ // If we can cache this bitmapGlyph, store it.
+ // Note : We will call this API once per each glyph.
+ if(ableUseCachedRenderedGlyph)
+ {
+ mGlyphCacheManager->CacheRenderedGlyphBuffer(glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
+
+ GlyphCacheManager::GlyphCacheData dummyData;
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, dummyData, error);
+
+ if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer))
+ {
+ data.buffer = dummyData.mRenderedBuffer->buffer;
+ data.width = dummyData.mRenderedBuffer->width;
+ data.height = dummyData.mRenderedBuffer->height;
+ data.format = dummyData.mRenderedBuffer->format;
+ data.compressionType = dummyData.mRenderedBuffer->compressionType;
+ data.isBufferOwned = false;
+ }
+ else
+ {
+ // Something problem during cache or get rendered glyph buffer.
+ // Move bitmap buffer into data.buffer
+ ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+ }
+ }
+ else
+ {
+ // Move bitmap buffer into data.buffer
+ ConvertBitmap(data, bitmapGlyph->bitmap, isShearRequired, true);
+ }
+
+ // Copied FT_Glyph object must be released with FT_Done_Glyph
+ FT_Done_Glyph(glyph);
+ }
+ else
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::CreateBitmap. FT_Get_Glyph Failed with error: %d\n", error);
+ }
}
}
else
* limitations under the License.
*/
+// CLASS HEADER
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
+
// INTERNAL INCLUDES
#include <dali/integration-api/debug.h>
#include <dali/internal/imaging/common/image-operations.h>
#include <dali/internal/text/text-abstraction/plugin/font-client-utils.h>
-#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
// EXTERNAL INCLUDES
#include FT_BITMAP_H
{
namespace
{
+constexpr uint32_t THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION = 8; // The smallest width of glyph that we use RLE4 method.
} // namespace
GlyphCacheManager::GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache)
{
DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
}
+
GlyphCacheManager::~GlyphCacheManager()
{
while(!mLRUGlyphCache.IsEmpty())
}
bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
- const GlyphIndex& index,
- const FT_Int32& flag,
- const bool& isBoldRequired,
- GlyphCacheData& glyphData,
- FT_Error& error)
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& glyphData,
+ FT_Error& error)
{
// Append some error value here instead of FT_Err_Ok.
error = static_cast<FT_Error>(-1);
}
bool GlyphCacheManager::LoadGlyphDataFromIndex(
- const GlyphIndex& index,
- const FT_Int32& flag,
- const bool& isBoldRequired,
- GlyphCacheData& glyphData,
- FT_Error& error)
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& glyphData,
+ FT_Error& error)
{
error = FT_Load_Glyph(mFreeTypeFace, index, flag);
if(FT_Err_Ok == error)
if(bufferSize > 0)
{
glyphData.mIsBitmap = true;
- glyphData.mBitmap->buffer = new uint8_t[bufferSize];
+ glyphData.mBitmap->buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
memcpy(glyphData.mBitmap->buffer, mFreeTypeFace->glyph->bitmap.buffer, bufferSize);
}
else
}
void GlyphCacheManager::ResizeBitmapGlyph(
- const GlyphIndex& index,
- const FT_Int32& flag,
- const bool& isBoldRequired,
- const uint32_t& desiredWidth,
- const uint32_t& desiredHeight)
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const uint32_t desiredWidth,
+ const uint32_t desiredHeight)
{
+ if(desiredWidth * desiredHeight <= 0)
+ {
+ // Skip this API if desired size is zero
+ return;
+ }
FT_Error error;
GlyphCacheData originGlyphData;
if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
const bool requiredResize = (originGlyphData.mBitmap->rows != desiredHeight) || (originGlyphData.mBitmap->width != desiredWidth);
if(requiredResize)
{
+ // originalGlyphData is copy data. For change cached information, we should access as iterator.
const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired);
auto iter = mLRUGlyphCache.Find(key);
{
if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width))
{
- desiredBuffer = new uint8_t[desiredWidth * desiredHeight];
+ desiredBuffer = (uint8_t*)malloc(desiredWidth * desiredHeight * sizeof(uint8_t)); // @note The caller is responsible for deallocating the bitmap data using free.
// Resize bitmap here.
Dali::Internal::Platform::LanczosSample1BPP(destinationGlpyhData.mBitmap->buffer,
inputDimensions,
{
if(destinationGlpyhData.mBitmap->pitch == static_cast<int>(destinationGlpyhData.mBitmap->width << 2u))
{
- desiredBuffer = new uint8_t[(desiredWidth * desiredHeight) << 2u];
+ desiredBuffer = (uint8_t*)malloc((desiredWidth * desiredHeight * sizeof(uint8_t)) << 2u); // @note The caller is responsible for deallocating the bitmap data using free.
// Resize bitmap here.
Dali::Internal::Platform::LanczosSample4BPP(destinationGlpyhData.mBitmap->buffer,
inputDimensions,
{
// Success to resize bitmap glyph.
// Release origin bitmap buffer.
- delete[] destinationGlpyhData.mBitmap->buffer;
+ free(destinationGlpyhData.mBitmap->buffer);
// Replace as desired buffer and size.
destinationGlpyhData.mBitmap->buffer = desiredBuffer;
}
}
+void GlyphCacheManager::CacheRenderedGlyphBuffer(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const FT_Bitmap& srcBitmap,
+ const CompressionPolicyType policy)
+{
+ if(srcBitmap.width * srcBitmap.rows <= 0)
+ {
+ // Skip this API if rendered bitmap size is zero
+ return;
+ }
+ FT_Error error;
+ GlyphCacheData originGlyphData;
+ if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+ {
+ if(DALI_LIKELY(!originGlyphData.mIsBitmap && originGlyphData.mRenderedBuffer == nullptr))
+ {
+ // originalGlyphData is copy data. For change cached information, we should access as iterator.
+ const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired);
+ auto iter = mLRUGlyphCache.Find(key);
+
+ GlyphCacheData& destinationGlpyhData = iter->element;
+
+ destinationGlpyhData.mRenderedBuffer = new TextAbstraction::FontClient::GlyphBufferData();
+ if(DALI_UNLIKELY(!destinationGlpyhData.mRenderedBuffer))
+ {
+ DALI_LOG_ERROR("Allocate GlyphBufferData failed\n");
+ return;
+ }
+
+ TextAbstraction::FontClient::GlyphBufferData& renderBuffer = *destinationGlpyhData.mRenderedBuffer;
+
+ // Set basic informations.
+ renderBuffer.width = srcBitmap.width;
+ renderBuffer.height = srcBitmap.rows;
+
+ switch(srcBitmap.pixel_mode)
+ {
+ case FT_PIXEL_MODE_GRAY:
+ {
+ renderBuffer.format = Pixel::L8;
+
+ if(policy == CompressionPolicyType::SPEED)
+ {
+ // If policy is SPEED, we will not compress bitmap.
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ }
+ else
+ {
+ // If small enough glyph, compress as BPP4 method.
+ if(srcBitmap.width < THRESHOLD_WIDTH_FOR_RLE4_COMPRESSION)
+ {
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::BPP_4;
+ }
+ else
+ {
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::RLE_4;
+ }
+ }
+
+ const auto compressedBufferSize = TextAbstraction::FontClient::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
+ if(DALI_UNLIKELY(compressedBufferSize == 0u))
+ {
+ DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_GRAY");
+ DALI_LOG_ERROR("Compress failed. Ignore cache\n");
+ delete destinationGlpyhData.mRenderedBuffer;
+ destinationGlpyhData.mRenderedBuffer = nullptr;
+ return;
+ }
+ break;
+ }
+#ifdef FREETYPE_BITMAP_SUPPORT
+ case FT_PIXEL_MODE_BGRA:
+ {
+ // Copy buffer without compress
+ renderBuffer.compressionType = TextAbstraction::FontClient::GlyphBufferData::CompressionType::NO_COMPRESSION;
+ renderBuffer.format = Pixel::BGRA8888;
+
+ const auto compressedBufferSize = TextAbstraction::FontClient::GlyphBufferData::Compress(srcBitmap.buffer, renderBuffer);
+ if(DALI_UNLIKELY(compressedBufferSize == 0u))
+ {
+ DALI_ASSERT_DEBUG(0 == "Compress failed at FT_PIXEL_MODE_BGRA");
+ DALI_LOG_ERROR("Compress failed. Ignore cache\n");
+ delete destinationGlpyhData.mRenderedBuffer;
+ destinationGlpyhData.mRenderedBuffer = nullptr;
+ return;
+ }
+ break;
+ }
+#endif
+ default:
+ {
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::General, "FontClient::Plugin::GlyphCacheManager::CacheRenderedGlyphBuffer. FontClient Unable to create Bitmap of this PixelType\n");
+ delete destinationGlpyhData.mRenderedBuffer;
+ destinationGlpyhData.mRenderedBuffer = nullptr;
+ break;
+ }
+ }
+ }
+ }
+}
+
void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
{
if(mIsBitmap && mBitmap)
{
// Created FT_Bitmap object must be released with FT_Bitmap_Done
- delete[] mBitmap->buffer;
+ free(mBitmap->buffer); // This buffer created by malloc
+
delete mBitmap;
mBitmap = nullptr;
}
FT_Done_Glyph(mGlyph);
mGlyph = nullptr;
}
+
+ if(mRenderedBuffer)
+ {
+ delete mRenderedBuffer;
+ }
}
} // namespace Dali::TextAbstraction::Internal
*/
// INTERNAL INCLUDES
+#include <dali/devel-api/text-abstraction/font-client.h> // For GlyphBufferData
+#include <dali/devel-api/text-abstraction/text-abstraction-definitions.h>
#include <dali/internal/text/text-abstraction/plugin/lru-cache-container.h>
// EXTERNAL INCLUDES
-
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
FT_Int32 mStyleFlags{0}; // Get from FT_Face
bool mIsBitmap{false};
+ TextAbstraction::FontClient::GlyphBufferData* mRenderedBuffer{nullptr}; // Rendered glyph buffer. Cached only if system allow to cache and we rendered it before. Otherwise, just nullptr
+
/**
* @brief Release the memory of loaded mGlyph / mBitmap.
*/
void ReleaseGlyphData();
};
+ // Compression priority of rendered glyph buffer.
+ enum class CompressionPolicyType
+ {
+ SPEED = 0,
+ MEMORY = 1,
+ };
+
public:
// Public API area.
* @return True if load successfully. False if something error occured.
*/
bool GetGlyphCacheDataFromIndex(
- const GlyphIndex& index,
- const FT_Int32& flag,
- const bool& isBoldRequired,
- GlyphCacheData& data,
- FT_Error& error);
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& data,
+ FT_Error& error);
/**
* @brief Load GlyphCacheData from face. The result will not be cached.
* @return True if load successfully. False if something error occured.
*/
bool LoadGlyphDataFromIndex(
- const GlyphIndex& index,
- const FT_Int32& flag,
- const bool& isBoldRequired,
- GlyphCacheData& data,
- FT_Error& error);
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ GlyphCacheData& data,
+ FT_Error& error);
/**
* @brief Resize bitmap glyph. The result will change cached glyph bitmap information.
* @param[in] desiredHeight Desired height of bitmap.
*/
void ResizeBitmapGlyph(
- const GlyphIndex& index,
- const FT_Int32& flag,
- const bool& isBoldRequired,
- const uint32_t& desiredWidth,
- const uint32_t& desiredHeight);
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const uint32_t desiredWidth,
+ const uint32_t desiredHeight);
+
+ /**
+ * @brief Cache rendered glyph bitmap. The result will change cached glyph information.
+ * If glyph is not single color glyph, or we already cached buffer before, nothing happened.
+ *
+ * @param[in] index Index of glyph in this face.
+ * @param[in] flag Flag when we load the glyph.
+ * @param[in] isBoldRequired True if we require some software bold.
+ * @param[in] srcBitmap Rendered glyph bitmap.
+ * @param[in] policy Compress behavior policy.
+ */
+ void CacheRenderedGlyphBuffer(
+ const GlyphIndex index,
+ const FT_Int32 flag,
+ const bool isBoldRequired,
+ const FT_Bitmap& srcBitmap,
+ const CompressionPolicyType policy);
private:
// Private struct area.
{
}
- GlyphCacheKey(const GlyphIndex& index, const FT_Int32& flag, const bool& boldRequired)
+ GlyphCacheKey(const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
: mIndex(index),
mFlag(flag),
mIsBoldRequired(boldRequired)
*/
// EXTERNAL INCLUDES
+#include <dali/public-api/common/dali-common.h>
+#include <dali/public-api/common/vector-wrapper.h>
#include <limits> // for std::numeric_limits
#include <unordered_map>
-#include <vector>
namespace Dali::TextAbstraction::Internal
{
DisplayConnectionEcoreWl::DisplayConnectionEcoreWl()
: mDisplay(NULL),
mSurfaceType(RenderSurfaceInterface::WINDOW_RENDER_SURFACE),
- mGraphics(nullptr)
+ mGraphics(nullptr),
+ mBufMgr(nullptr)
{
}
EGLNativeDisplayType DisplayConnectionEcoreWl::GetNativeDisplay()
{
+ mBufMgr = tbm_bufmgr_init(-1); // -1 is meaningless. The parameter in this function is deprecated.
+ if(mBufMgr == nullptr)
+ {
+ DALI_LOG_ERROR("Fail to init tbm buf mgr\n");
+ return nullptr;
+ }
return reinterpret_cast<EGLNativeDisplayType>(tbm_dummy_display_create());
}
{
tbm_dummy_display_destroy(reinterpret_cast<tbm_dummy_display*>(mDisplay));
}
+
+ if(mBufMgr != nullptr)
+ {
+ tbm_bufmgr_deinit(mBufMgr);
+ }
}
} // namespace Adaptor
*
*/
+// EXTERNAL INCLUDES
+#include <tbm_bufmgr.h>
+
// INTERNAL INCLUDES
#include <dali/internal/window-system/common/display-connection-impl.h>
EGLNativeDisplayType mDisplay; ///< Wayland-display for rendering
Dali::RenderSurfaceInterface::Type mSurfaceType; ///< The surface type
GraphicsInterface* mGraphics; ///< The graphics interface
+ tbm_bufmgr mBufMgr; ///< For creating tbm_dummy_display
};
} // namespace Adaptor
{
const unsigned int ADAPTOR_MAJOR_VERSION = 2;
const unsigned int ADAPTOR_MINOR_VERSION = 1;
-const unsigned int ADAPTOR_MICRO_VERSION = 28;
+const unsigned int ADAPTOR_MICRO_VERSION = 29;
const char* const ADAPTOR_BUILD_DATE = __DATE__ " " __TIME__;
#ifdef DEBUG_ENABLED
Name: dali2-adaptor
Summary: The DALi Tizen Adaptor
-Version: 2.1.28
+Version: 2.1.29
Release: 1
Group: System/Libraries
License: Apache-2.0 and BSD-3-Clause and MIT