/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ // Purpose: // - Manage and cache glyphs, retrieving them from the renderer as needed #if !defined(USE_NULLFONT_ALWAYS) #include #include //------------------------------------------------------------------------------------------------- AZ::GlyphCache::GlyphCache() : m_usage(1) , m_glyphBitmapWidth(0) , m_glyphBitmapHeight(0) , m_scaleBitmap(0) { m_cacheTable.clear(); m_slotList.clear(); } //------------------------------------------------------------------------------------------------- AZ::GlyphCache::~GlyphCache() { } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::Create(int iCacheSize, int glyphBitmapWidth, int glyphBitmapHeight, AZ::FontSmoothMethod smoothMethod, AZ::FontSmoothAmount smoothAmount, float sizeRatio) { m_smoothMethod = smoothMethod; m_smoothAmount = smoothAmount; m_glyphBitmapWidth = glyphBitmapWidth; m_glyphBitmapHeight = glyphBitmapHeight; if (!CreateSlotList(iCacheSize)) { ReleaseSlotList(); return 0; } int iScaledGlyphWidth = 0; int iScaledGlyphHeight = 0; switch (m_smoothMethod) { case AZ::FontSmoothMethod::SuperSample: { switch (m_smoothAmount) { case AZ::FontSmoothAmount::x2: iScaledGlyphWidth = m_glyphBitmapWidth << 1; iScaledGlyphHeight = m_glyphBitmapHeight << 1; break; case AZ::FontSmoothAmount::x4: iScaledGlyphWidth = m_glyphBitmapWidth << 2; iScaledGlyphHeight = m_glyphBitmapHeight << 2; break; } } break; } if (iScaledGlyphWidth) { m_scaleBitmap = new GlyphBitmap; if (!m_scaleBitmap) { Release(); return 0; } if (!m_scaleBitmap->Create(iScaledGlyphWidth, iScaledGlyphHeight)) { Release(); return 0; } m_fontRenderer.SetGlyphBitmapSize(iScaledGlyphWidth, iScaledGlyphHeight, sizeRatio); } else { m_fontRenderer.SetGlyphBitmapSize(m_glyphBitmapWidth, m_glyphBitmapHeight, sizeRatio); } return 1; } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::Release() { ReleaseSlotList(); m_cacheTable.clear(); if (m_scaleBitmap) { m_scaleBitmap->Release(); delete m_scaleBitmap; m_scaleBitmap = 0; } m_glyphBitmapWidth = 0; m_glyphBitmapHeight = 0; return 1; } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::LoadFontFromFile(const AZStd::string & fileName) { return m_fontRenderer.LoadFromFile(fileName); } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::LoadFontFromMemory(unsigned char* fileBuffer, int dataSize) { return m_fontRenderer.LoadFromMemory(fileBuffer, dataSize); } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::ReleaseFont() { m_fontRenderer.Release(); return 1; } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::GetGlyphBitmapSize(int* width, int* height) { if (width) { *width = m_glyphBitmapWidth; } if (height) { *height = m_glyphBitmapHeight; } return 1; } //------------------------------------------------------------------------------------------------- void AZ::GlyphCache::SetGlyphBitmapSize(int width, int height, float sizeRatio) { m_fontRenderer.SetGlyphBitmapSize(width, height, sizeRatio); } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::PreCacheGlyph(uint32_t character, const AtomFont::GlyphSize& glyphSize, const FFont::FontHintParams& fontHintParams) { CacheTable::iterator pItor = m_cacheTable.find(GetCacheSlotKey(character, glyphSize)); if (pItor != m_cacheTable.end()) { pItor->second->m_usage = m_usage; return 1; } CacheSlot* slot = GetLRUSlot(); if (!slot) { return 0; } if (slot->m_usage > 0) { UnCacheGlyph(slot->m_currentCharacter, slot->m_glyphSize); } if (m_scaleBitmap) { int iOffsetMult = 1; switch (m_smoothAmount) { case AZ::FontSmoothAmount::x2: iOffsetMult = 2; break; case AZ::FontSmoothAmount::x4: iOffsetMult = 4; break; } m_scaleBitmap->Clear(); if (!m_fontRenderer.GetGlyph(m_scaleBitmap, &slot->m_horizontalAdvance, &slot->m_characterWidth, &slot->m_characterHeight, slot->m_characterOffsetX, slot->m_characterOffsetY, 0, 0, character, fontHintParams)) { return 0; } slot->m_characterWidth >>= iOffsetMult >> 1; slot->m_characterHeight >>= iOffsetMult >> 1; m_scaleBitmap->BlitScaledTo8(slot->m_glyphBitmap.GetBuffer(), 0, 0, m_scaleBitmap->GetWidth(), m_scaleBitmap->GetHeight(), 0, 0, slot->m_glyphBitmap.GetWidth(), slot->m_glyphBitmap.GetHeight(), slot->m_glyphBitmap.GetWidth()); } else { if (!m_fontRenderer.GetGlyph(&slot->m_glyphBitmap, &slot->m_horizontalAdvance, &slot->m_characterWidth, &slot->m_characterHeight, slot->m_characterOffsetX, slot->m_characterOffsetY, 0, 0, character, fontHintParams)) { return 0; } } if (m_smoothMethod == AZ::FontSmoothMethod::Blur) { slot->m_glyphBitmap.Blur(m_smoothAmount); } slot->m_usage = m_usage; slot->m_currentCharacter = character; slot->m_glyphSize = glyphSize; m_cacheTable.insert(AZStd::pair(GetCacheSlotKey(character, glyphSize), slot)); return 1; } int AZ::GlyphCache::UnCacheGlyph(uint32_t character, const AtomFont::GlyphSize& glyphSize) { CacheTable::iterator pItor = m_cacheTable.find(GetCacheSlotKey(character, glyphSize)); if (pItor != m_cacheTable.end()) { CacheSlot* slot = pItor->second; slot->Reset(); m_cacheTable.erase(pItor); return 1; } return 0; } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::GlyphCached(uint32_t character, const AtomFont::GlyphSize& glyphSize) { return (m_cacheTable.find(GetCacheSlotKey(character, glyphSize)) != m_cacheTable.end()); } //------------------------------------------------------------------------------------------------- AZ::CacheSlot* AZ::GlyphCache::GetLRUSlot() { unsigned int dwMinUsage = 0xffffffff; CacheSlot* pLRUSlot = 0; CacheSlot* slot; CacheSlotListItor pItor = m_slotList.begin(); while (pItor != m_slotList.end()) { slot = *pItor; if (slot->m_usage == 0) { return slot; } else { if (slot->m_usage < dwMinUsage) { pLRUSlot = slot; dwMinUsage = slot->m_usage; } } pItor++; } return pLRUSlot; } //------------------------------------------------------------------------------------------------- AZ::CacheSlot* AZ::GlyphCache::GetMRUSlot() { unsigned int dwMaxUsage = 0; CacheSlot* pMRUSlot = 0; CacheSlot* slot; CacheSlotListItor pItor = m_slotList.begin(); while (pItor != m_slotList.end()) { slot = *pItor; if (slot->m_usage != 0) { if (slot->m_usage > dwMaxUsage) { pMRUSlot = slot; dwMaxUsage = slot->m_usage; } } pItor++; } return pMRUSlot; } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::GetGlyph(AZ::GlyphBitmap** glyph, int* horizontalAdvance, int* width, int* height, int32_t& m_characterOffsetX, int32_t& m_characterOffsetY, uint32_t character, const AZ::AtomFont::GlyphSize& glyphSize, const AZ::FFont::FontHintParams& fontHintParams) { CacheTable::iterator pItor = m_cacheTable.find(GetCacheSlotKey(character, glyphSize)); if (pItor == m_cacheTable.end()) { if (!PreCacheGlyph(character, glyphSize, fontHintParams)) { return 0; } } pItor = m_cacheTable.find(GetCacheSlotKey(character, glyphSize)); pItor->second->m_usage = m_usage++; (*glyph) = &pItor->second->m_glyphBitmap; if (horizontalAdvance) { *horizontalAdvance = pItor->second->m_horizontalAdvance; } if (width) { *width = pItor->second->m_characterWidth; } if (height) { *height = pItor->second->m_characterHeight; } m_characterOffsetX = pItor->second->m_characterOffsetX; m_characterOffsetY = pItor->second->m_characterOffsetY; return 1; } //------------------------------------------------------------------------------------------------- Vec2 AZ::GlyphCache::GetKerning(uint32_t leftGlyph, uint32_t rightGlyph) { return m_fontRenderer.GetKerning(leftGlyph, rightGlyph); } //------------------------------------------------------------------------------------------------- float AZ::GlyphCache::GetAscenderToHeightRatio() { return m_fontRenderer.GetAscenderToHeightRatio(); } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::CreateSlotList(int listSize) { for (int i = 0; i < listSize; i++) { CacheSlot* cacheSlot = new CacheSlot; if (!cacheSlot) { return 0; } if (!cacheSlot->m_glyphBitmap.Create(m_glyphBitmapWidth, m_glyphBitmapHeight)) { delete cacheSlot; return 0; } cacheSlot->Reset(); cacheSlot->m_slotIndex = i; m_slotList.push_back(cacheSlot); } return 1; } //------------------------------------------------------------------------------------------------- int AZ::GlyphCache::ReleaseSlotList() { CacheSlotListItor pItor = m_slotList.begin(); while (pItor != m_slotList.end()) { (*pItor)->m_glyphBitmap.Release(); delete (*pItor); pItor = m_slotList.erase(pItor); } return 1; } //------------------------------------------------------------------------------------------------- AZ::GlyphCache::CacheTableKey AZ::GlyphCache::GetCacheSlotKey(uint32_t character, const AZ::AtomFont::GlyphSize& glyphSize) const { const AZ::AtomFont::GlyphSize clampedGlyphSize = AZ::FontTexture::ClampGlyphSize(glyphSize, m_glyphBitmapWidth, m_glyphBitmapHeight); return CacheTableKey(clampedGlyphSize, character); } #endif // #if !defined(USE_NULLFONT_ALWAYS)