Files
FlaxEngine/Source/Engine/Render2D/FontAsset.cpp
2024-02-18 19:48:43 +01:00

249 lines
5.8 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#include "FontAsset.h"
#include "Font.h"
#include "FontManager.h"
#include "Engine/Core/Log.h"
#include "Engine/Content/Content.h"
#include "Engine/Content/Factories/BinaryAssetFactory.h"
#include "Engine/Content/Upgraders/FontAssetUpgrader.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Threading/Threading.h"
#include "IncludeFreeType.h"
#if USE_EDITOR
#include "Engine/Platform/FileSystem.h"
#endif
REGISTER_BINARY_ASSET_WITH_UPGRADER(FontAsset, "FlaxEngine.FontAsset", FontAssetUpgrader, true);
FontAsset::FontAsset(const SpawnParams& params, const AssetInfo* info)
: BinaryAsset(params, info)
, _face(nullptr)
{
}
String FontAsset::GetFamilyName() const
{
return _face ? String(_face->family_name) : String::Empty;
}
String FontAsset::GetStyleName() const
{
return _face ? String(_face->style_name) : String::Empty;
}
Asset::LoadResult FontAsset::load()
{
// Load font data
auto chunk0 = GetChunk(0);
if (chunk0 == nullptr || chunk0->IsMissing())
return LoadResult::MissingDataChunk;
_fontFile.Swap(chunk0->Data);
// Create font face
return Init() ? LoadResult::Failed : LoadResult::Ok;
}
void FontAsset::unload(bool isReloading)
{
// Ensure to cleanup child font objects
if (_fonts.HasItems())
{
LOG(Warning, "Font asset {0} is unloading but has {1} remaining font objects created", ToString(), _fonts.Count());
for (auto font : _fonts)
{
font->_asset = nullptr;
font->DeleteObject();
}
_fonts.Clear();
}
// Unload face
if (_face)
{
FT_Done_Face(_face);
_face = nullptr;
}
// Cleanup data
_fontFile.Release();
_virtualBold = nullptr;
_virtualItalic = nullptr;
}
AssetChunksFlag FontAsset::getChunksToPreload() const
{
return GET_CHUNK_FLAG(0);
}
bool FontAsset::Init()
{
const FT_Error error = FT_New_Memory_Face(FontManager::GetLibrary(), _fontFile.Get(), static_cast<FT_Long>(_fontFile.Length()), 0, &_face);
if (error)
{
_face = nullptr;
LOG_FT_ERROR(error);
}
return error;
}
FontFlags FontAsset::GetStyle() const
{
FontFlags flags = FontFlags::None;
if ((_face->style_flags & FT_STYLE_FLAG_ITALIC) != 0)
flags |= FontFlags::Italic;
if ((_face->style_flags & FT_STYLE_FLAG_BOLD) != 0)
flags |= FontFlags::Bold;
return flags;
}
void FontAsset::SetOptions(const FontOptions& value)
{
_options = value;
}
Font* FontAsset::CreateFont(float size)
{
PROFILE_CPU();
if (WaitForLoaded())
return nullptr;
ScopeLock lock(Locker);
if (_face == nullptr)
return nullptr;
// Check if font with that size has already been created
for (auto font : _fonts)
{
if (font->GetAsset() == this && font->GetSize() == size)
return font;
}
return New<Font>(this, size);
}
FontAsset* FontAsset::GetBold()
{
ScopeLock lock(Locker);
if (EnumHasAnyFlags(_options.Flags, FontFlags::Bold))
return this;
if (!_virtualBold)
{
_virtualBold = Content::CreateVirtualAsset<FontAsset>();
_virtualBold->Init(_fontFile);
auto options = _options;
options.Flags |= FontFlags::Bold;
_virtualBold->SetOptions(options);
}
return _virtualBold;
}
FontAsset* FontAsset::GetItalic()
{
ScopeLock lock(Locker);
if (EnumHasAnyFlags(_options.Flags, FontFlags::Italic))
return this;
if (!_virtualItalic)
{
_virtualItalic = Content::CreateVirtualAsset<FontAsset>();
_virtualItalic->Init(_fontFile);
auto options = _options;
options.Flags |= FontFlags::Italic;
_virtualItalic->SetOptions(options);
}
return _virtualItalic;
}
bool FontAsset::Init(const BytesContainer& fontFile)
{
ScopeLock lock(Locker);
unload(true);
_fontFile.Copy(fontFile);
return Init();
}
#if USE_EDITOR
bool FontAsset::Save(const StringView& path)
{
// Validate state
if (WaitForLoaded())
{
LOG(Error, "Asset loading failed. Cannot save it.");
return true;
}
if (IsVirtual() && path.IsEmpty())
{
LOG(Error, "To save virtual asset asset you need to specify the target asset path location.");
return true;
}
ScopeLock lock(Locker);
AssetInitData data;
data.SerializedVersion = SerializedVersion;
data.CustomData.Copy(&_options);
auto chunk0 = GetChunk(0);
_fontFile.Swap(chunk0->Data);
const bool saveResult = path.HasChars() ? SaveAsset(path, data) : SaveAsset(data, true);
_fontFile.Swap(chunk0->Data);
if (saveResult)
{
LOG(Error, "Cannot save '{0}'", ToString());
return true;
}
return false;
}
#endif
bool FontAsset::ContainsChar(Char c) const
{
return FT_Get_Char_Index(_face, c) > 0;
}
void FontAsset::Invalidate()
{
ScopeLock lock(Locker);
for (auto font : _fonts)
font->Invalidate();
}
uint64 FontAsset::GetMemoryUsage() const
{
Locker.Lock();
uint64 result = BinaryAsset::GetMemoryUsage();
result += sizeof(FontAsset) - sizeof(BinaryAsset);
result += sizeof(FT_FaceRec);
result += _fontFile.Length();
for (auto font : _fonts)
result += sizeof(Font);
Locker.Unlock();
return result;
}
bool FontAsset::init(AssetInitData& initData)
{
if (IsVirtual())
return false;
// Validate
if (initData.SerializedVersion != SerializedVersion)
{
LOG(Error, "Invalid serialized font asset version.");
return true;
}
if (initData.CustomData.Length() != sizeof(_options))
{
LOG(Error, "Missing font asset header.");
return true;
}
// Copy header
Platform::MemoryCopy(&_options, initData.CustomData.Get(), sizeof(_options));
return false;
}