Currently, glyph cache manager owned by each font-face-cache-item.
That mean, we can hold glyph cache as
{The number of font type} x {The number of font size} x 128(default).
This patch make we hold GlyphCacheManager hold as singletone.
So we can cache the glyph maximum 128(default) not relative with
the number of font type & size.
Change-Id: I85e3cec3d160a57496e839f0b87a24655861f981
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
#include <fontconfig/fontconfig.h>
// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
#include <dali/devel-api/adaptor-framework/image-loading.h>
#include <dali/internal/text/text-abstraction/font-client-impl.h>
#include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
namespace
{
+/**
+ * @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 = 3u;
+
+constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
+
+/**
+ * @brief Get maximum size of glyph cache size from environment.
+ * If not settuped, default as 128.
+ * @note This value fixed when we call it first time.
+ * @return The max size of glyph cache.
+ */
+inline 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;
+}
+
} // namespace
namespace Dali::TextAbstraction::Internal
mFontDescriptionSizeCache(),
mEllipsisCache(),
mEmbeddedItemCache(),
+ mGlyphCacheManager(new GlyphCacheManager(GetMaxNumberOfGlyphCache())),
mLatestFoundFontDescription(),
mLatestFoundFontDescriptionId(0u),
mLatestFoundCacheKey(0, 0),
void FontClient::Plugin::CacheHandler::ClearCache()
{
+ // delete cached glyph informations before clear mFontFaceCache.
+ mGlyphCacheManager->ClearCache();
+
mDefaultFontDescription = FontDescription();
mSystemFonts.clear();
// INTERNAL INCLUDES
#include <dali/internal/text/text-abstraction/plugin/font-client-plugin-impl.h>
+#include <dali/internal/text/text-abstraction/plugin/font-face-glyph-cache-manager.h>
namespace Dali::TextAbstraction::Internal
{
*/
GlyphIndex CacheEmbeddedItem(EmbeddedItem&& embeddedItem);
+public: // Other public API
+ GlyphCacheManager* GetGlyphCacheManager() const
+ {
+ return mGlyphCacheManager.get();
+ }
+
private:
CacheHandler(const CacheHandler&) = delete;
CacheHandler& operator=(const CacheHandler&) = delete;
std::vector<PixelBufferCacheItem> mPixelBufferCache; ///< Caches the pixel buffer of a url.
std::vector<EmbeddedItem> mEmbeddedItemCache; ///< Cache embedded items.
-private: // Member value
+private: // Member value
+ std::unique_ptr<GlyphCacheManager> mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs.
+
FontDescription mLatestFoundFontDescription; ///< Latest found font description and id in FindValidatedFont()
FontDescriptionId mLatestFoundFontDescriptionId;
const float fixedHeight = static_cast<float>(ftFace->available_sizes[fixedSizeIndex].height);
// Create the FreeType font face item to cache.
- FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
+ FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics, fixedSizeIndex, fixedWidth, fixedHeight, hasColorTables);
fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
}
static_cast<float>(ftFace->underline_thickness) * FROM_266);
// Create the FreeType font face item to cache.
- FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, path, requestedPointSize, faceIndex, metrics);
+ FontFaceCacheItem fontFaceCacheItem(mFreeTypeLibrary, ftFace, mCacheHandler->GetGlyphCacheManager(), path, requestedPointSize, faceIndex, metrics);
fontId = mCacheHandler->CacheFontFaceCacheItem(std::move(fontFaceCacheItem));
}
constexpr float MAXIMUM_RATE_OF_BITMAP_GLYPH_CACHE_RESIZE = 1.5f;
/**
- * @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 = 3u;
-
-constexpr auto MAX_NUMBER_OF_GLYPH_CACHE_ENV = "DALI_GLYPH_CACHE_MAX";
-
-/**
- * @brief Get maximum size of glyph cache size from environment.
- * If not settuped, default as 128.
- * @note This value fixed when we call it first time.
- * @return The max size of glyph cache.
- */
-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;
* @note This value fixed when we call it first time.
* @return True if we allow to cache rendered glyph.
*/
-inline const bool EnableCacheRenderedGlyph()
+inline bool EnableCacheRenderedGlyph()
{
using Dali::EnvironmentVariable::GetEnvironmentVariable;
static auto numberString = GetEnvironmentVariable(ENABLE_CACHE_RENDERED_GLYPH_ENV);
* @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()
+inline GlyphCacheManager::CompressionPolicyType GetRenderedGlyphCompressPolicy()
{
using Dali::EnvironmentVariable::GetEnvironmentVariable;
static auto policyString = GetEnvironmentVariable(RENDERED_GLYPH_COMPRESS_POLICY_ENV);
FontFaceCacheItem::FontFaceCacheItem(const FT_Library& freeTypeLibrary,
FT_Face ftFace,
+ GlyphCacheManager* glyphCacheManager,
const FontPath& path,
PointSize26Dot6 requestedPointSize,
FaceIndex face,
const FontMetrics& metrics)
: mFreeTypeLibrary(freeTypeLibrary),
mFreeTypeFace(ftFace),
- mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
+ mGlyphCacheManager(glyphCacheManager),
mHarfBuzzProxyFont(),
mPath(path),
mRequestedPointSize(requestedPointSize),
FontFaceCacheItem::FontFaceCacheItem(const FT_Library& freeTypeLibrary,
FT_Face ftFace,
+ GlyphCacheManager* glyphCacheManager,
const FontPath& path,
PointSize26Dot6 requestedPointSize,
FaceIndex face,
bool hasColorTables)
: mFreeTypeLibrary(freeTypeLibrary),
mFreeTypeFace(ftFace),
- mGlyphCacheManager(new GlyphCacheManager(mFreeTypeFace, GetMaxNumberOfGlyphCache())),
+ mGlyphCacheManager(glyphCacheManager),
mHarfBuzzProxyFont(),
mPath(path),
mRequestedPointSize(requestedPointSize),
: mFreeTypeLibrary(rhs.mFreeTypeLibrary)
{
mFreeTypeFace = rhs.mFreeTypeFace;
- mGlyphCacheManager = std::move(rhs.mGlyphCacheManager);
+ mGlyphCacheManager = rhs.mGlyphCacheManager;
mHarfBuzzProxyFont = std::move(rhs.mHarfBuzzProxyFont);
mPath = std::move(rhs.mPath);
mRequestedPointSize = rhs.mRequestedPointSize;
mIsFixedSizeBitmap = rhs.mIsFixedSizeBitmap;
mHasColorTables = rhs.mHasColorTables;
- rhs.mFreeTypeFace = nullptr;
+ rhs.mFreeTypeFace = nullptr;
+ rhs.mGlyphCacheManager = nullptr;
}
FontFaceCacheItem::~FontFaceCacheItem()
{
- // delete glyph cache manager before free face.
+ // delete cached glyph informations before free face.
if(mGlyphCacheManager)
{
- mGlyphCacheManager.reset();
+ mGlyphCacheManager->RemoveGlyphFromFace(mFreeTypeFace);
}
+ // delete harfbuzz proxy font before free face.
if(mHarfBuzzProxyFont)
{
mHarfBuzzProxyFont.reset();
if(mIsFixedSizeBitmap)
{
FT_Select_Size(mFreeTypeFace, mFixedSizeIndex); ///< @todo: needs to be investigated why it's needed to select the size again.
- mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error);
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, glyphData, error);
if(FT_Err_Ok == error)
{
// TODO : If dpiVertical value changed, this resize feature will be break down.
// Otherwise, this glyph will be resized only one times.
- mGlyphCacheManager->ResizeBitmapGlyph(glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
+ mGlyphCacheManager->ResizeBitmapGlyph(mFreeTypeFace, glyphInfo.index, FT_LOAD_COLOR, glyphInfo.isBoldRequired, static_cast<uint32_t>(glyphInfo.width), static_cast<uint32_t>(glyphInfo.height));
}
}
}
// 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(glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, glyphInfo.isBoldRequired, glyphData, error);
// Keep the width of the glyph before doing the software emboldening.
// It will be used to calculate a scale factor to be applied to the
{
// Get dummy glyph data without embolden.
GlyphCacheManager::GlyphCacheData dummyData;
- if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
+ if(mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphInfo.index, FT_LOAD_NO_AUTOHINT, false, dummyData, error))
{
// If the glyph is emboldened by software, the advance is multiplied by a
// scale factor to make it slightly bigger.
// @todo: add an option to use the FT_LOAD_DEFAULT if required?
loadFlag = FT_LOAD_NO_AUTOHINT;
}
- mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, glyphData, error);
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, glyphData, error);
if(FT_Err_Ok == error)
{
// Note : We will call this API once per each glyph.
if(ableUseCachedRenderedGlyph)
{
- mGlyphCacheManager->CacheRenderedGlyphBuffer(glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
+ mGlyphCacheManager->CacheRenderedGlyphBuffer(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, bitmapGlyph->bitmap, GetRenderedGlyphCompressPolicy());
GlyphCacheManager::GlyphCacheData dummyData;
- mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, loadFlag, isBoldRequired, dummyData, error);
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, loadFlag, isBoldRequired, dummyData, error);
if(DALI_LIKELY(FT_Err_Ok == error && dummyData.mRenderedBuffer))
{
if(mHasColorTables)
{
GlyphCacheManager::GlyphCacheData dummyData;
- mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
+ mGlyphCacheManager->GetGlyphCacheDataFromIndex(mFreeTypeFace, glyphIndex, FT_LOAD_COLOR, false, dummyData, error);
}
#endif
return FT_Err_Ok == error;
// Create new harfbuzz font only first time or DPI changed.
if(DALI_UNLIKELY(!mHarfBuzzProxyFont || mHarfBuzzProxyFont->mHorizontalDpi != horizontalDpi || mHarfBuzzProxyFont->mVerticalDpi != verticalDpi))
{
- mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager.get()));
+ mHarfBuzzProxyFont.reset(new HarfBuzzProxyFont(mFreeTypeFace, mRequestedPointSize, horizontalDpi, verticalDpi, mGlyphCacheManager));
}
return mHarfBuzzProxyFont->GetHarfBuzzFont();
}
{
FontFaceCacheItem(const FT_Library& freeTypeLibrary,
FT_Face ftFace,
+ GlyphCacheManager* glyphCacheManager,
const FontPath& path,
PointSize26Dot6 requestedPointSize,
FaceIndex face,
FontFaceCacheItem(const FT_Library& freeTypeLibrary,
FT_Face ftFace,
+ GlyphCacheManager* glyphCacheManager,
const FontPath& path,
PointSize26Dot6 requestedPointSize,
FaceIndex face,
const FT_Library& mFreeTypeLibrary; ///< A handle to a FreeType library instance.
FT_Face mFreeTypeFace; ///< The FreeType face.
- std::unique_ptr<GlyphCacheManager> mGlyphCacheManager; ///< The glyph cache manager. It will cache this face's glyphs.
+ GlyphCacheManager* mGlyphCacheManager; ///< The reference of Glyph cache manager. Owned from font-client-plugin-cache-handler.
std::unique_ptr<HarfBuzzProxyFont> mHarfBuzzProxyFont; ///< The harfbuzz font. It will store harfbuzz relate data.
FontPath mPath; ///< The path to the font file name.
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)
-: mFreeTypeFace(ftFace),
- mGlyphCacheMaxSize(maxNumberOfGlyphCache),
+GlyphCacheManager::GlyphCacheManager(std::size_t maxNumberOfGlyphCache)
+: mGlyphCacheMaxSize(maxNumberOfGlyphCache),
mLRUGlyphCache(mGlyphCacheMaxSize)
{
DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager Create with maximum size : %d\n", static_cast<int>(mGlyphCacheMaxSize));
GlyphCacheManager::~GlyphCacheManager()
{
- while(!mLRUGlyphCache.IsEmpty())
- {
- auto removedData = mLRUGlyphCache.Pop();
-
- // Release Glyph data resource
- removedData.ReleaseGlyphData();
- }
- mLRUGlyphCache.Clear();
+ ClearCache();
}
bool GlyphCacheManager::GetGlyphCacheDataFromIndex(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
// Append some error value here instead of FT_Err_Ok.
error = static_cast<FT_Error>(-1);
- const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired);
+ const GlyphCacheKey key = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
auto iter = mLRUGlyphCache.Find(key);
if(iter == mLRUGlyphCache.End())
removedData.ReleaseGlyphData();
}
- const bool loadSuccess = LoadGlyphDataFromIndex(index, flag, isBoldRequired, glyphData, error);
+ const bool loadSuccess = LoadGlyphDataFromIndex(freeTypeFace, index, flag, isBoldRequired, glyphData, error);
if(loadSuccess)
{
// Copy and cached data.
mLRUGlyphCache.Push(key, glyphData);
- DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Create cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
}
return loadSuccess;
error = FT_Err_Ok;
// We already notify that we use this glyph. And now, copy cached data.
- glyphData = iter->element;
+ glyphData = mLRUGlyphCache.GetElement(iter);
- DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::GetGlyphCacheDataFromIndex. Find cache for face : %p, index : %u flag : %d isBold : %d isBitmap : %d, glyph : %p\n", freeTypeFace, index, static_cast<int>(flag), isBoldRequired, glyphData.mIsBitmap, glyphData.mGlyph);
return true;
}
}
bool GlyphCacheManager::LoadGlyphDataFromIndex(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
GlyphCacheData& glyphData,
FT_Error& error)
{
- error = FT_Load_Glyph(mFreeTypeFace, index, flag);
+ error = FT_Load_Glyph(freeTypeFace, index, flag);
if(FT_Err_Ok == error)
{
- glyphData.mStyleFlags = mFreeTypeFace->style_flags;
+ glyphData.mStyleFlags = freeTypeFace->style_flags;
const bool isEmboldeningRequired = isBoldRequired && !(glyphData.mStyleFlags & FT_STYLE_FLAG_BOLD);
if(isEmboldeningRequired)
{
// Does the software bold.
- FT_GlyphSlot_Embolden(mFreeTypeFace->glyph);
+ FT_GlyphSlot_Embolden(freeTypeFace->glyph);
}
- glyphData.mGlyphMetrics = mFreeTypeFace->glyph->metrics;
+ glyphData.mGlyphMetrics = freeTypeFace->glyph->metrics;
glyphData.mIsBitmap = false;
// Load glyph
- error = FT_Get_Glyph(mFreeTypeFace->glyph, &glyphData.mGlyph);
+ error = FT_Get_Glyph(freeTypeFace->glyph, &glyphData.mGlyph);
if(glyphData.mGlyph->format == FT_GLYPH_FORMAT_BITMAP)
{
// Copy rendered bitmap
// TODO : Is there any way to keep bitmap buffer without copy?
glyphData.mBitmap = new FT_Bitmap();
- *glyphData.mBitmap = mFreeTypeFace->glyph->bitmap;
+ *glyphData.mBitmap = freeTypeFace->glyph->bitmap;
// New allocate buffer
size_t bufferSize = 0;
{
glyphData.mIsBitmap = true;
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);
+ memcpy(glyphData.mBitmap->buffer, freeTypeFace->glyph->bitmap.buffer, bufferSize);
}
else
{
}
void GlyphCacheManager::ResizeBitmapGlyph(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
}
FT_Error error;
GlyphCacheData originGlyphData;
- if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+ if(GetGlyphCacheDataFromIndex(freeTypeFace, index, flag, isBoldRequired, originGlyphData, error))
{
if(DALI_LIKELY(originGlyphData.mIsBitmap && originGlyphData.mBitmap))
{
if(requiredResize)
{
// originalGlyphData is copy data. For change cached information, we should access as iterator.
- const GlyphCacheKey key = GlyphCacheKey(index, flag, isBoldRequired);
+ const GlyphCacheKey key = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
auto iter = mLRUGlyphCache.Find(key);
- GlyphCacheData& destinationGlpyhData = iter->element;
+ GlyphCacheData& destinationGlpyhData = mLRUGlyphCache.GetElement(iter);
const ImageDimensions inputDimensions(destinationGlpyhData.mBitmap->width, destinationGlpyhData.mBitmap->rows);
const ImageDimensions desiredDimensions(desiredWidth, desiredHeight);
}
void GlyphCacheManager::CacheRenderedGlyphBuffer(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
}
FT_Error error;
GlyphCacheData originGlyphData;
- if(GetGlyphCacheDataFromIndex(index, flag, isBoldRequired, originGlyphData, error))
+ if(GetGlyphCacheDataFromIndex(freeTypeFace, 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);
+ const GlyphCacheKey key = GlyphCacheKey(freeTypeFace, index, flag, isBoldRequired);
auto iter = mLRUGlyphCache.Find(key);
- GlyphCacheData& destinationGlpyhData = iter->element;
+ GlyphCacheData& destinationGlpyhData = mLRUGlyphCache.GetElement(iter);
destinationGlpyhData.mRenderedBuffer = new TextAbstraction::FontClient::GlyphBufferData();
if(DALI_UNLIKELY(!destinationGlpyhData.mRenderedBuffer))
}
}
+void GlyphCacheManager::RemoveGlyphFromFace(const FT_Face freeTypeFace)
+{
+ uint32_t removedItemCount = 0;
+
+ auto endIter = mLRUGlyphCache.End();
+ for(auto iter = mLRUGlyphCache.Begin(); iter != endIter;)
+ {
+ // Check whether this cached item has inputed freeTypeFace as key.
+ auto keyFace = mLRUGlyphCache.GetKey(iter).mFreeTypeFace;
+ if(keyFace == freeTypeFace)
+ {
+ ++removedItemCount;
+ iter = mLRUGlyphCache.Erase(iter);
+ }
+ else
+ {
+ ++iter;
+ }
+ }
+
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::RemoveGlyphFromFace. Remove all cached glyph with face : %p, removed glyph count : %u\n", freeTypeFace, removedItemCount);
+}
+
+void GlyphCacheManager::ClearCache(const std::size_t remainCount)
+{
+ if(remainCount == 0u)
+ {
+ // Release all data memory first.
+ auto endIter = mLRUGlyphCache.End();
+ for(auto iter = mLRUGlyphCache.Begin(); iter != endIter; ++iter)
+ {
+ // Get the reference of data. and release it.
+ auto& removedData = mLRUGlyphCache.GetElement(iter);
+ removedData.ReleaseGlyphData();
+ }
+
+ // Clear all cache.
+ mLRUGlyphCache.Clear();
+ }
+ else
+ {
+ // While the cache count is bigger than remainCount, remove oldest glyph.
+ while(mLRUGlyphCache.Count() > remainCount)
+ {
+ auto removedData = mLRUGlyphCache.Pop();
+
+ DALI_LOG_INFO(gFontClientLogFilter, Debug::Verbose, "FontClient::Plugin::GlyphCacheManager::ClearCache[%zu / %zu]. Remove oldest cache for glyph : %p\n", mLRUGlyphCache.Count(), remainCount, removedData.mGlyph);
+
+ // Release Glyph data resource
+ removedData.ReleaseGlyphData();
+ }
+ }
+}
+
void GlyphCacheManager::GlyphCacheData::ReleaseGlyphData()
{
if(mIsBitmap && mBitmap)
if(mRenderedBuffer)
{
delete mRenderedBuffer;
+ mRenderedBuffer = nullptr;
}
+
+ mStyleFlags = 0;
}
} // namespace Dali::TextAbstraction::Internal
{
public:
// Constructor
- GlyphCacheManager(FT_Face ftFace, std::size_t maxNumberOfGlyphCache);
+ GlyphCacheManager(std::size_t maxNumberOfGlyphCache);
// Destructor
~GlyphCacheManager();
/**
* @brief Load GlyphCacheData from face. The result will be cached.
*
+ * @param[in] freeTypeFace The freetype face handle.
* @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.
* @return True if load successfully. False if something error occured.
*/
bool GetGlyphCacheDataFromIndex(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
* @brief Load GlyphCacheData from face. The result will not be cached.
* @note If we call this API, We should release GlyphCacheData manually.
*
+ * @param[in] freeTypeFace The freetype face handle.
* @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.
* @return True if load successfully. False if something error occured.
*/
bool LoadGlyphDataFromIndex(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
* @brief Resize bitmap glyph. The result will change cached glyph bitmap information.
* If glyph is not bitmap glyph, nothing happened.
*
+ * @param[in] freeTypeFace The freetype face handle.
* @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] desiredHeight Desired height of bitmap.
*/
void ResizeBitmapGlyph(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
* @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] freeTypeFace The freetype face handle.
* @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] policy Compress behavior policy.
*/
void CacheRenderedGlyphBuffer(
+ const FT_Face freeTypeFace,
const GlyphIndex index,
const FT_Int32 flag,
const bool isBoldRequired,
const FT_Bitmap& srcBitmap,
const CompressionPolicyType policy);
+ /**
+ * @brief Clear all cached glyph informations which has inputed FreeTypeFace.
+ *
+ * @note This API iterate all cached glyph. Should be called rarely.
+ * @param[in] freeTypeFace The freetype face handle.
+ */
+ void RemoveGlyphFromFace(const FT_Face freeTypeFace);
+
+ /**
+ * @brief Clear all cached glyph informations.
+ *
+ * @param[in] remainCount The number of remained cache items after call this API. Default is 0, clear all items.
+ */
+ void ClearCache(const std::size_t remainCount = 0u);
+
private:
// Private struct area.
/**
struct GlyphCacheKey
{
GlyphCacheKey()
- : mIndex(0u),
+ : mFreeTypeFace(nullptr),
+ mIndex(0u),
mFlag(0),
mIsBoldRequired(false)
{
}
- GlyphCacheKey(const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
- : mIndex(index),
+ GlyphCacheKey(const FT_Face freeTypeFace, const GlyphIndex index, const FT_Int32 flag, const bool boldRequired)
+ : mFreeTypeFace(freeTypeFace),
+ mIndex(index),
mFlag(flag),
mIsBoldRequired(boldRequired)
{
}
+
+ FT_Face mFreeTypeFace;
GlyphIndex mIndex;
FT_Int32 mFlag;
bool mIsBoldRequired : 1;
bool operator==(GlyphCacheKey const& rhs) const noexcept
{
- return mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired;
+ return mFreeTypeFace == rhs.mFreeTypeFace && mIndex == rhs.mIndex && mFlag == rhs.mFlag && mIsBoldRequired == rhs.mIsBoldRequired;
}
};
{
std::size_t operator()(GlyphCacheKey const& key) const noexcept
{
- return static_cast<std::size_t>(key.mIndex) ^ static_cast<std::size_t>(key.mFlag) ^ (static_cast<std::size_t>(key.mIsBoldRequired) << 29);
+ return static_cast<std::size_t>(reinterpret_cast<std::uintptr_t>(key.mFreeTypeFace)) ^
+ static_cast<std::size_t>(key.mIndex) ^
+ static_cast<std::size_t>(key.mFlag) ^
+ (static_cast<std::size_t>(key.mIsBoldRequired) << 29);
}
};
private:
// Private member value area.
- FT_Face mFreeTypeFace; ///< The FreeType face. Owned from font-face-cache-item
-
std::size_t mGlyphCacheMaxSize; ///< The maximum capacity of glyph cache.
using CacheContainer = LRUCacheContainer<GlyphCacheKey, GlyphCacheData, GlyphCacheKeyHash>;
public:
FT_Face mFreeTypeFace; ///< The FreeType face. Owned from font-face-cache-item.
- GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-face-cache-item.
+ GlyphCacheManager* mGlyphCacheManager; ///< Glyph caching system for this harfbuzz font. Owned from font-client-plugin-cache-handler.
hb_font_t* mHarfBuzzFont; ///< Harfbuzz font handle integrated with FT_Face.
};
if(DALI_LIKELY(impl && impl->mGlyphCacheManager))
{
FT_Error error;
- return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error);
+ return impl->mGlyphCacheManager->GetGlyphCacheDataFromIndex(impl->mFreeTypeFace, glyphIndex, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING, false, glyphData, error);
}
return false;
}
{
public:
// Constructor
- LRUCacheContainer(std::size_t maxNumberOfCache = std::numeric_limits<std::size_t>::max())
+ LRUCacheContainer(std::size_t maxNumberOfCache = std::numeric_limits<std::size_t>::max() - 2u)
: mCacheMaxSize(maxNumberOfCache)
{
}
LRUCacheContainer(const LRUCacheContainer& rhs) = default;
LRUCacheContainer(LRUCacheContainer&& rhs) = default;
+ LRUCacheContainer& operator=(const LRUCacheContainer& rhs) = default;
+ LRUCacheContainer& operator=(LRUCacheContainer&& rhs) = default;
+
public:
// Public struct area.
using CacheId = std::size_t; ///< The id of cached element. It can be used until element poped out.
*/
static constexpr CacheId CACHE_FOOTER_ID = std::numeric_limits<std::size_t>::max() - 1u;
+public:
+ // iterator.
/**
- * @brief Double linked CacheNode that this container used.
+ * @brief Iterator of this LRU cache.
+ *
+ * Add, Get and Clear invalidate the itertator's validation.
+ * Erase and Pop validate the iterator.
+ *
+ * Range based iteration doesn't supported.
*/
- struct CacheNode
+ struct iterator
{
- CacheNode() = default;
- ~CacheNode() = default;
+ public:
+ iterator(LRUCacheContainer& owner, const CacheId& id)
+ : owner(owner),
+ id(id)
+ {
+ }
- CacheId prev{CACHE_FOOTER_ID};
- CacheId next{CACHE_HEADER_ID};
- ElementType element;
+ // copy constructor & assign
+ iterator(const iterator& rhs)
+ : owner(rhs.owner),
+ id(rhs.id)
+ {
+ }
- using CacheIdIterator = typename CacheIdContainer::iterator;
+ iterator& operator=(const iterator& rhs)
+ {
+ this->owner = rhs.owner;
+ this->id = rhs.id;
+ return *this;
+ }
- CacheIdIterator cacheIdIterator; ///< Note : It only validate until mCacheId rehashing.
- };
+ // Move constructor & assign
+ iterator(iterator&& rhs)
+ : owner(std::move(rhs.owner)),
+ id(rhs.id)
+ {
+ }
+
+ iterator& operator=(iterator&& rhs)
+ {
+ this->owner = std::move(rhs.owner);
+ this->id = rhs.id;
+ return *this;
+ }
- using iterator = typename std::vector<CacheNode>::iterator;
+ // Prefix increment
+ iterator& operator++()
+ {
+ id = owner.mData[id].next;
+ return *this;
+ }
+
+ // Postfix increment
+ iterator operator++(int)
+ {
+ iterator temp = *this;
+ ++(*this);
+ return temp;
+ }
+
+ // Prefix decrement
+ iterator& operator--()
+ {
+ id = owner.mData[id].prev;
+ return *this;
+ }
+
+ // Postfix decrement
+ iterator operator--(int)
+ {
+ iterator temp = *this;
+ --(*this);
+ return temp;
+ }
+
+ bool operator==(const iterator& rhs)
+ {
+ return id == rhs.id && (&owner) == (&rhs.owner);
+ }
+ bool operator!=(const iterator& rhs)
+ {
+ return id != rhs.id || (&owner) != (&rhs.owner);
+ }
+
+ public:
+ LRUCacheContainer& owner; // The reference of owner of this iterator.
+ CacheId id;
+ };
public:
// Public API area.
}
/**
- * @brief Find an element by the key. It will be marked as recent
+ * @brief Get an key by iterator. It will not be marked as recent
+ *
+ * @param[in] iter The iterator of element
+ * @return A reference of the key
+ * @warning This method don't check iterator validation
+ */
+ const KeyType& GetKey(iterator iter)
+ {
+ const auto id = iter.id;
+
+ return mData[id].cacheIdIterator->first;
+ }
+
+ /**
+ * @brief Get an element by iterator. It will not be marked as recent
+ *
+ * @param[in] iter The iterator of element
+ * @return A reference of the element
+ * @warning This method don't check iterator validation
+ */
+ ElementType& GetElement(iterator iter)
+ {
+ const auto id = iter.id;
+
+ return mData[id].element;
+ }
+
+ /**
+ * @brief Find an element by the key. It will not be marked as recent
*
* @param[in] key The key of element
* @return A iterator of cache node. If key not exist, return End()
const auto id = mCacheId[key];
- // Mark as recently used.
- InternalPop(id);
- InternalInsertAfterHeader(id);
-
- return Begin() + id;
+ return iterator(*this, id);
}
/**
iterator Begin()
{
- return mData.begin();
+ return iterator(*this, mLatestId);
}
iterator End()
{
- return mData.end();
+ return iterator(*this, CACHE_FOOTER_ID);
+ }
+
+ /**
+ * @brief Remove cache item by iterator.
+ *
+ * @param[in] iter The iterator what we want to remove.
+ * @return iterator The next iterator after remove
+ * @warning This method don't check iterator validation
+ */
+ iterator Erase(iterator iter)
+ {
+ const auto id = iter.id;
+ const auto nextId = mData[id].next;
+ InternalPop(id);
+ InternalInsertAfterFooter(id);
+
+ --mNumberOfElements;
+
+ // Erase cache id.
+ mCacheId.erase(mData[id].cacheIdIterator);
+
+ return iterator(*this, nextId);
}
/**
}
private:
- // Private struct area.
-
-private:
// Private API area.
/**
}
private:
+ // Private struct area.
+ /**
+ * @brief Double linked CacheNode that this container used.
+ */
+ struct CacheNode
+ {
+ CacheNode() = default;
+ ~CacheNode() = default;
+
+ CacheId prev{CACHE_FOOTER_ID};
+ CacheId next{CACHE_HEADER_ID};
+ ElementType element;
+
+ using CacheIdIterator = typename CacheIdContainer::iterator;
+
+ CacheIdIterator cacheIdIterator; ///< Note : It only validate until mCacheId rehashing.
+ };
+
+private:
// Private member value area.
std::size_t mCacheMaxSize{0}; ///< The maximum capacity of cache.
std::size_t mNumberOfElements{0}; ///< The number of elements.