• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef __STDC_FORMAT_MACROS
17 #define __STDC_FORMAT_MACROS
18 #endif
19 
20 #include "font_manager.h"
21 
22 #include <algorithm>
23 #include <freetype/ftsizes.h>
24 #include <freetype/tttables.h>
25 #include <numeric>
26 
27 #include <base/containers/fixed_string.h>
28 #include <base/containers/string.h>
29 #include <core/intf_engine.h>
30 #include <core/io/intf_file_manager.h>
31 #include <core/log.h>
32 #include <render/datastore/intf_render_data_store_default_staging.h>
33 #include <render/datastore/intf_render_data_store_manager.h>
34 #include <render/device/gpu_resource_desc.h>
35 #include <render/device/intf_gpu_resource_manager.h>
36 #include <render/intf_render_context.h>
37 
38 #include "face_data.h"
39 #include "font.h"
40 #include "font_buffer.h"
41 
42 using namespace BASE_NS;
43 using namespace BASE_NS::Math;
44 using namespace CORE_NS;
45 using namespace RENDER_NS;
46 using namespace FONT_NS::FontDefs;
47 
48 FONT_BEGIN_NAMESPACE()
49 
50 namespace {
GetErrorString()51 string GetErrorString()
52 {
53     // Visual Studio lacks strerrorlen_s so need to define a fixed "large enough" buffer
54     char buffer[64u];
55 #ifdef _WIN32
56     strerror_s(buffer, sizeof(buffer), errno);
57 #else
58     strerror_r(errno, buffer, sizeof(buffer));
59 #endif
60     return string(buffer);
61 }
62 
63 constexpr string_view RENDER_DATA_STORE_DEFAULT_STAGING = "RenderDataStoreDefaultStaging";
64 
65 constexpr Width widths_os2_match[] = { Width(0), Width::UltraCondensed, Width::ExtraCondensed, Width::Condensed,
66     Width::SemiCondensed, Width::Normal, Width::SemiExpanded, Width::Expanded, Width::ExtraExpanded,
67     Width::UltraExpanded };
68 
InitTypeFace(FT_Face face,string_view path,string_view filename)69 TypeFace InitTypeFace(FT_Face face, string_view path, string_view filename)
70 {
71     TypeFace typeFace {
72         string(path),            // font file path
73         face->family_name,       // family name as defined in font file
74         face->style_name,        // style name as defined in font file
75         BASE_NS::hash(filename), // physical PATH hash as map key for font buffers map
76         face->face_index,        // face index for quick access to face in font file
77         Regular,                 // style
78     };
79     const TT_OS2* tblOS2 = static_cast<TT_OS2*>(FT_Get_Sfnt_Table(face, FT_SFNT_OS2));
80     if (tblOS2) {
81         typeFace.style.Weight = static_cast<Weight>(tblOS2->usWeightClass);
82         typeFace.style.Width = widths_os2_match[tblOS2->usWidthClass];
83     } else {
84         if (face->style_flags & FT_STYLE_FLAG_BOLD) {
85             typeFace.style.Weight = Weight::Bold;
86         }
87     }
88     if (face->style_flags & FT_STYLE_FLAG_ITALIC) {
89         typeFace.style.SlantType = SlantType::Italic;
90     }
91     return typeFace;
92 }
93 } // namespace
94 
FontManager(IRenderContext & context)95 FontManager::FontManager(IRenderContext& context) : renderContext_(context)
96 {
97     if (FT_Init_FreeType(&fontLib_)) {
98         CORE_LOG_E("failed to init freetype library: %s", GetErrorString().c_str());
99     }
100 
101     GetTypeFacesByDir(typeFaces_, "fonts://");
102 }
103 
~FontManager()104 FontManager::~FontManager()
105 {
106     FT_Done_FreeType(fontLib_);
107     fontLib_ = nullptr;
108 }
109 
GetTypeFaces() const110 array_view<const TypeFace> FontManager::GetTypeFaces() const
111 {
112     return typeFaces_;
113 }
114 
GetTypeFace(string_view name,string_view styleName)115 const TypeFace* FontManager::GetTypeFace(string_view name, string_view styleName)
116 {
117     for (auto& typeFace : typeFaces_) {
118         if (typeFace.name == name) {
119             if (styleName.empty() || typeFace.styleName == styleName) {
120                 return &typeFace;
121             }
122         }
123     }
124     return nullptr;
125 }
126 
GetTypeFaces(const string_view filePath)127 vector<TypeFace> FontManager::GetTypeFaces(const string_view filePath)
128 {
129     vector<TypeFace> typeFaces;
130     GetTypeFacesByFile(typeFaces, filePath);
131     return typeFaces;
132 }
133 
GetTypeFaces(array_view<const string_view> lookupDirs)134 vector<TypeFace> FontManager::GetTypeFaces(array_view<const string_view> lookupDirs)
135 {
136     vector<TypeFace> typeFaces;
137     for (const auto& dir : lookupDirs) {
138         GetTypeFacesByDir(typeFaces, dir);
139     }
140     return typeFaces;
141 }
142 
CreateFontBuffer(const TypeFace & typeFace)143 std::shared_ptr<FontBuffer> FontManager::CreateFontBuffer(const TypeFace& typeFace)
144 {
145     {
146         std::shared_lock readerLock(fontBuffersMutex_);
147 
148         if (auto it = fontBuffers_.find(typeFace.uid); it != fontBuffers_.end()) {
149             if (auto fromWeak = it->second.lock()) {
150                 return fromWeak;
151             }
152         }
153     }
154 
155     std::lock_guard writerLock(fontBuffersMutex_);
156 
157     if (auto it = fontBuffers_.find(typeFace.uid); it != fontBuffers_.end()) {
158         if (auto fromWeak = it->second.lock()) {
159             return fromWeak;
160         }
161     }
162 
163     auto& fileManager = renderContext_.GetEngine().GetFileManager();
164     IFile::Ptr file = fileManager.OpenFile(typeFace.path);
165     if (file == nullptr) {
166         CORE_LOG_E("failed to open font %s | %s", typeFace.path.data(), GetErrorString().c_str());
167         return nullptr;
168     }
169 
170     const size_t len = static_cast<size_t>(file->GetLength());
171     vector<uint8_t> bytes;
172     bytes.resize(len);
173 
174     if (len != file->Read(bytes.data(), bytes.size())) {
175         CORE_LOG_E("failed to read %zu bytes from %s | %s", len, typeFace.path.data(), GetErrorString().c_str());
176         return nullptr;
177     }
178 
179     auto fb = std::make_shared<FontBuffer>(this, typeFace.uid, bytes);
180 
181     fontBuffers_.insert({ typeFace.uid, fb });
182     CORE_LOG_N("create FontBuffer");
183 
184     return fb;
185 }
186 
CreateFont(const TypeFace & typeFace)187 IFont::Ptr FontManager::CreateFont(const TypeFace& typeFace)
188 {
189     std::shared_ptr fontBuff = CreateFontBuffer(typeFace);
190     if (!fontBuff) {
191         return nullptr;
192     }
193     std::shared_ptr<FaceData> face = fontBuff->CreateFace(typeFace.index);
194     if (!face) {
195         return nullptr;
196     }
197     Font::Ptr font = Font::Ptr(new Font(std::move(face)));
198 
199     return font;
200 }
201 
CreateFontFromMemory(const TypeFace & typeFace,const BASE_NS::vector<uint8_t> & buffer)202 IFont::Ptr FontManager::CreateFontFromMemory(const TypeFace& typeFace, const BASE_NS::vector<uint8_t>& buffer)
203 {
204     std::shared_ptr<FontBuffer> fontBuff;
205     {
206         std::shared_lock readerLock(fontBuffersMutex_);
207         if (auto it = fontBuffers_.find(typeFace.uid); it != fontBuffers_.end()) {
208             fontBuff = it->second.lock();
209         }
210     }
211     if (!fontBuff) {
212         std::lock_guard writerLock(fontBuffersMutex_);
213         if (auto it = fontBuffers_.find(typeFace.uid); it != fontBuffers_.end()) {
214             fontBuff = it->second.lock();
215         }
216         if (!fontBuff) {
217             fontBuff = std::make_shared<FontBuffer>(this, typeFace.uid, buffer);
218             fontBuffers_.insert({ typeFace.uid, fontBuff });
219         }
220     }
221     if (!fontBuff) {
222         return {};
223     }
224 
225     return Font::Ptr(new Font(fontBuff->CreateFace(typeFace.index)));
226 }
227 
GetGlyphIndex(const TypeFace & typeFace,uint32_t code)228 uint32_t FontManager::GetGlyphIndex(const TypeFace& typeFace, uint32_t code)
229 {
230     auto fontBuff = CreateFontBuffer(typeFace);
231     if (!fontBuff) {
232         return 0;
233     }
234     auto face = fontBuff->CreateFace(typeFace.index);
235     if (!face) {
236         return 0;
237     }
238     return face->GetGlyphIndex(code);
239 }
240 
FlushCaches()241 void FontManager::FlushCaches()
242 {
243     UploadPending();
244     std::lock_guard writerLock(atlasMutex_);
245     atlasTextures_.clear();
246     CORE_LOG_N("atlas textures flush");
247 }
248 
GetTypeFacesByFile(vector<TypeFace> & typeFaces,string_view path)249 void FontManager::GetTypeFacesByFile(vector<TypeFace>& typeFaces, string_view path)
250 {
251     auto& fileManager = renderContext_.GetEngine().GetFileManager();
252 
253     IFile::Ptr file = fileManager.OpenFile(path);
254     if (file == nullptr) {
255         CORE_LOG_E("failed to open font %s | %s", path.data(), GetErrorString().c_str());
256         return;
257     }
258 
259     auto entry = fileManager.GetEntry(path);
260     const size_t len = static_cast<size_t>(file->GetLength());
261     vector<uint8_t> buf(len);
262 
263     if (len != file->Read(buf.data(), buf.size())) {
264         CORE_LOG_E("failed to read %zu bytes from %s | %s", len, path.data(), GetErrorString().c_str());
265         return;
266     }
267 
268     if (buf.empty()) {
269         return;
270     }
271 
272     FT_Long numFaces = 0;
273     FT_Long numInstances = 0;
274     FT_Long faceIndex = 0;
275     FT_Long instanceIndex = 0;
276 
277     constexpr uint8_t INSTANCE_SHIFT = 16u;
278 
279     do {
280         FT_Long faceId = (instanceIndex << INSTANCE_SHIFT) + faceIndex;
281         auto face = OpenFtFace(buf, faceId);
282         // lume api is utf8, unicode is the default charmap with freetype.
283         if (face && face->charmap) {
284             typeFaces.push_back(InitTypeFace(face, path, entry.name));
285 
286             numFaces = face->num_faces;
287             numInstances = face->style_flags >> INSTANCE_SHIFT;
288 
289             CORE_LOG_N("%s", path.data());
290             CORE_LOG_N("  number of faces:     %ld", numFaces);
291             CORE_LOG_N("  number of instances: %ld", numInstances);
292 
293             if (instanceIndex < numInstances) {
294                 ++instanceIndex;
295             } else {
296                 ++faceIndex;
297                 instanceIndex = 0;
298             }
299             FT_Done_Face(face);
300         } else {
301             CORE_LOG_W("failed to create face: %s", path.data());
302         }
303     } while (faceIndex < numFaces);
304 }
305 
GetTypeFacesByDir(vector<TypeFace> & typeFaces,string_view path)306 void FontManager::GetTypeFacesByDir(vector<TypeFace>& typeFaces, string_view path)
307 {
308     auto& fileManager = renderContext_.GetEngine().GetFileManager();
309     IDirectory::Ptr dir = fileManager.OpenDirectory(path);
310     if (!dir) {
311         return;
312     }
313 
314     vector<IDirectory::Entry> const files = dir->GetEntries();
315     CORE_LOG_N("check dir '%s' with %zu entries", string(path).c_str(), files.size());
316 
317     for (auto const& file : files) {
318         if (file.type == IDirectory::Entry::Type::FILE) {
319             auto fontUri = path + file.name;
320             if (IsFont(fontUri)) {
321                 GetTypeFacesByFile(typeFaces, fontUri);
322             }
323         }
324     }
325 }
326 
327 /* Functions for streaming integration with freetype.
328  * (used to allow not reading the whole file just to check if it's a supported font file).
329  */
330 extern "C" {
FtRead(FT_Stream stream,unsigned long offset,unsigned char * buffer,unsigned long count)331 static unsigned long FtRead(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count)
332 {
333     unsigned long result = 0;
334     auto* file = reinterpret_cast<CORE_NS::IFile*>(stream->descriptor.pointer);
335     if (!file) {
336         return 0;
337     }
338 
339     auto seekResult = file->Seek(offset);
340     // Note: If count == 0, return the status of the seek operation (non-zero indicates an error).
341     if (count == 0) {
342         return !seekResult;
343     }
344 
345     if (!seekResult) {
346         // Seek failed.
347         return 0;
348     }
349     result = static_cast<unsigned long>(file->Read(buffer, count));
350     return result;
351 }
352 
FtClose(FT_Stream stream)353 static void FtClose(FT_Stream stream)
354 {
355     auto* file = reinterpret_cast<CORE_NS::IFile*>(stream->descriptor.pointer);
356     if (file) {
357         file->Close();
358     }
359 }
360 } // extern "C"
361 
362 namespace {
363 class FtFileReader {
364 public:
FtFileReader(CORE_NS::IFileManager & fileManager,BASE_NS::string_view uri)365     FtFileReader(CORE_NS::IFileManager& fileManager, BASE_NS::string_view uri) : fileManager_(fileManager), uri_(uri) {}
~FtFileReader()366     ~FtFileReader() {}
367 
Open()368     FT_Stream Open()
369     {
370         file_ = fileManager_.OpenFile(uri_);
371         if (!file_) {
372             return {};
373         }
374 
375         stream_.base = nullptr;
376         stream_.size = static_cast<unsigned long>(file_->GetLength());
377         stream_.pos = 0;
378         stream_.descriptor.pointer = file_.get();
379         stream_.pathname.pointer = (void*)uri_.data();
380         stream_.read = FtRead;
381         stream_.close = FtClose;
382 
383         return &stream_;
384     }
385 
386 private:
387     CORE_NS::IFileManager& fileManager_;
388     BASE_NS::string uri_;
389     CORE_NS::IFile::Ptr file_ {};
390     FT_StreamRec stream_ {};
391 };
392 } // namespace
393 
IsFont(BASE_NS::string_view uri)394 bool FontManager::IsFont(BASE_NS::string_view uri)
395 {
396     // FT_Open_Face and its siblings can be used to quickly check whether the font format of a given font resource
397     // is supported by FreeType. In general, if the face_index argument is negative, the function's return value is
398     // 0 if the font format is recognized, or non-zero otherwise.
399     return OpenFtFace(uri, -1, nullptr);
400 }
401 
OpenFtFace(BASE_NS::string_view uri,FT_Long index,FT_Face * face)402 bool FontManager::OpenFtFace(BASE_NS::string_view uri, FT_Long index, FT_Face* face)
403 {
404     CORE_ASSERT_MSG(fontLib_, "font library is not initialized");
405 
406     auto& fileManager = renderContext_.GetEngine().GetFileManager();
407 
408     FT_Open_Args args {};
409     args.flags = FT_OPEN_STREAM;
410 
411     FtFileReader reader(fileManager, uri);
412     args.stream = reader.Open();
413     if (!args.stream) {
414         CORE_LOG_E("failed to open font: %s | %s", BASE_NS::string(uri).c_str(), GetErrorString().c_str());
415         return false;
416     }
417 
418     return FT_Open_Face(fontLib_, &args, index, face) == 0;
419 }
420 
OpenFtFace(array_view<const uint8_t> buf,FT_Long index)421 FT_Face FontManager::OpenFtFace(array_view<const uint8_t> buf, FT_Long index)
422 {
423     CORE_ASSERT_MSG(fontLib_, "font library is not initialized");
424 
425     FT_Face face;
426     FT_Error err = FT_New_Memory_Face(fontLib_, buf.data(), (FT_Long)buf.size(), index, &face);
427     if (err) {
428         CORE_LOG_E("failed to init font face");
429         return nullptr;
430     }
431     if (!face->charmap) {
432         CORE_LOG_E("failed to init font face, no unicode charmap available");
433         FT_Done_Face(face);
434         return nullptr;
435     }
436 
437     CORE_ASSERT(face->num_glyphs > 0);
438 
439     return face;
440 }
441 
CreateAtlasTexture(bool color)442 FontManager::AtlasTexture* FontManager::CreateAtlasTexture(bool color)
443 {
444     // Access locked from caller already
445     auto tex = &atlasTextures_.emplace_back();
446     const ComponentMapping colorSwizzle { CORE_COMPONENT_SWIZZLE_B, CORE_COMPONENT_SWIZZLE_G, CORE_COMPONENT_SWIZZLE_R,
447         CORE_COMPONENT_SWIZZLE_A }; // BGRA swizzle
448     const ComponentMapping monoSwizzle { CORE_COMPONENT_SWIZZLE_R, CORE_COMPONENT_SWIZZLE_R, CORE_COMPONENT_SWIZZLE_R,
449         CORE_COMPONENT_SWIZZLE_R }; // RRRR swizzle
450     const GpuImageDesc desc {
451         ImageType::CORE_IMAGE_TYPE_2D,                                            // imageType
452         ImageViewType::CORE_IMAGE_VIEW_TYPE_2D,                                   // imageViewType
453         color ? Format::BASE_FORMAT_R8G8B8A8_SRGB : Format::BASE_FORMAT_R8_UNORM, // format
454         ImageTiling::CORE_IMAGE_TILING_OPTIMAL,                                   // imageTiling
455         ImageUsageFlagBits::CORE_IMAGE_USAGE_TRANSFER_DST_BIT |
456             ImageUsageFlagBits::CORE_IMAGE_USAGE_SAMPLED_BIT,                     // usageFlags
457         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,            // memoryPropertyFlags
458         0,                                                                        // ImageCreateFlags
459         EngineImageCreationFlagBits::CORE_ENGINE_IMAGE_CREATION_DYNAMIC_BARRIERS, // EngineImageCreationFlags
460         FontDefs::ATLAS_SIZE,                                                     // width
461         FontDefs::ATLAS_SIZE,                                                     // height
462         1,                                                                        // depth
463         1,                                                                        // mip count
464         1,                                                                        // layer count
465         SampleCountFlagBits::CORE_SAMPLE_COUNT_1_BIT,                             // sample count
466         color ? colorSwizzle : monoSwizzle                                        // swizzle
467     };
468     auto& gpuResourceManager = renderContext_.GetDevice().GetGpuResourceManager();
469     tex->handle = gpuResourceManager.Create(desc);
470     tex->widthLeft = FontDefs::ATLAS_SIZE;
471     tex->inColor = color;
472 
473 #if defined(FONT_VALIDATION_ENABLED) && (FONT_VALIDATION_ENABLED)
474     string atlasName = "FontAtlas:";
475     atlasName += to_string(reinterpret_cast<uintptr_t>(this));
476     atlasName += ':';
477     atlasName += to_string(atlasTextures_.size());
478     tex->name = atlasName;
479     CORE_LOG_N("Created atlas '%s' gpuHnd: %llx", tex->name.data(), tex->handle.GetHandle().id);
480 #endif
481 
482     // Clear the atlas as we don't initialize borders when copying glyphs
483     auto staging = refcnt_ptr<IRenderDataStoreDefaultStaging>(static_cast<IRenderDataStoreDefaultStaging*>(
484         renderContext_.GetRenderDataStoreManager().GetRenderDataStore(RENDER_DATA_STORE_DEFAULT_STAGING).get()));
485     ClearColorValue zero { 0.f, 0.f, 0.f, 0.f };
486     staging->ClearColorImage(tex->handle, zero);
487     return tex;
488 }
489 
UpdateAtlas(FontDefs::Glyph & glyph,const FT_Bitmap & bitmap,bool inColor)490 int FontManager::UpdateAtlas(FontDefs::Glyph& glyph, const FT_Bitmap& bitmap, bool inColor)
491 {
492     uint32_t width = bitmap.width + BORDER_WIDTH_X2;
493     uint32_t height = bitmap.rows + BORDER_WIDTH_X2;
494 
495     // Find best fit (but larger) column in atlases
496     uint32_t bestFitAtlas = 0;
497     uint32_t bestFitColumn = 0;
498     uint32_t bestFitColumnPos = 0;
499     uint32_t minDiff = UINT32_MAX;
500 
501     std::lock_guard writerLock(atlasMutex_);
502 
503     for (uint32_t i = 0; i < atlasTextures_.size(); ++i) {
504         auto& atlas = atlasTextures_[i];
505         if (inColor == atlas.inColor) {
506             uint32_t colPos = 0;
507             for (uint32_t col = 0; col < atlas.columns.size(); ++col) {
508                 auto& hdr = atlas.columns[col];
509 
510                 if (hdr.colWidth >= width && hdr.heightLeft >= height) {
511                     if (hdr.colWidth == width) { // Exact match
512                         AddGlyphToColumn(glyph, i, hdr, bitmap, colPos);
513                         return ATLAS_OK;
514                     }
515                     uint32_t wDiff = hdr.colWidth - width;
516                     if (wDiff < minDiff) {
517                         minDiff = wDiff;
518                         bestFitAtlas = i;
519                         bestFitColumn = col;
520                         bestFitColumnPos = colPos;
521                     }
522                 }
523                 colPos += hdr.colWidth;
524             }
525         }
526     }
527 
528     if (minDiff <= GLYPH_FIT_THRESHOLD) {
529         // Close enough
530         auto& atlas = atlasTextures_[bestFitAtlas];
531         auto& hdr = atlas.columns[bestFitColumn];
532         AddGlyphToColumn(glyph, bestFitAtlas, hdr, bitmap, bestFitColumnPos);
533         return ATLAS_OK;
534     }
535 
536     // No matching column is found try to create one ...
537     for (uint32_t i = 0; i < atlasTextures_.size(); ++i) {
538         auto& atlas = atlasTextures_[i];
539         if (inColor == atlas.inColor && atlas.widthLeft >= width) {
540             uint32_t colPos = FontDefs::ATLAS_SIZE - atlas.widthLeft;
541             atlas.widthLeft -= width;
542             auto& hdr = atlas.columns.emplace_back();
543             hdr.colWidth = static_cast<uint16_t>(width);
544             hdr.heightLeft = FontDefs::ATLAS_SIZE;
545             AddGlyphToColumn(glyph, i, hdr, bitmap, colPos);
546             return ATLAS_OK;
547         }
548     }
549 
550     // Need to create new atlas
551     size_t i = atlasTextures_.size();
552     auto atlas = CreateAtlasTexture(inColor);
553     atlas->widthLeft -= width;
554     auto& hdr = atlas->columns.emplace_back();
555     hdr.colWidth = static_cast<uint16_t>(width);
556     hdr.heightLeft = static_cast<uint16_t>(ATLAS_SIZE);
557     AddGlyphToColumn(glyph, i, hdr, bitmap, 0);
558     return ATLAS_OK;
559 }
560 
UploadPending()561 void FontManager::UploadPending()
562 {
563     std::lock_guard writerLock(atlasMutex_);
564     if (std::all_of(atlasTextures_.cbegin(), atlasTextures_.cend(),
565         [](const AtlasTexture& atlas) { return atlas.pending.empty(); })) {
566         return;
567     }
568 
569     struct ColumnSize {
570         uint32_t index;
571         uint32_t count;
572         uint16_t width;
573         uint16_t height;
574         uint32_t byteSize;
575     };
576     BASE_NS::vector<BASE_NS::vector<ColumnSize>> allColumnWidths;
577     allColumnWidths.reserve(atlasTextures_.size());
578     uint32_t totalSize = 0U;
579     for (auto& atlas : atlasTextures_) {
580         // sort so that columns can be identified
581         std::sort(atlas.pending.begin(), atlas.pending.end(), [](const PendingGlyph& lhs, const PendingGlyph& rhs) {
582             if (lhs.posX < rhs.posX) {
583                 return true;
584             }
585             if (lhs.posX > rhs.posX) {
586                 return false;
587             }
588             if (lhs.posY < rhs.posY) {
589                 return true;
590             }
591             if (lhs.posY > rhs.posY) {
592                 return false;
593             }
594             return false;
595         });
596 
597         auto& columnWidths = allColumnWidths.emplace_back();
598         columnWidths.reserve(atlas.pending.size());
599 
600         auto first = atlas.pending.begin();
601         auto last = atlas.pending.end();
602         for (; first != last;) {
603             // find the first glyph in the column
604             first = std::lower_bound(first, last, *(last - 1),
605                 [](const PendingGlyph& value, const PendingGlyph& element) { return value.posX < element.posX; });
606             // check the maximum width of the column
607             const auto width = std::accumulate(first, last, uint16_t(0u),
608                 [](const uint16_t a, const PendingGlyph& b) { return std::max(a, b.width); });
609             const auto height = static_cast<uint16_t>(((last - 1)->posY + (last - 1)->height) - first->posY);
610             const auto bufferSize = static_cast<uint32_t>(width * height * (atlas.inColor ? 4u : 1u));
611             totalSize += bufferSize;
612             columnWidths.push_back({ static_cast<uint32_t>(std::distance(atlas.pending.begin(), first)),
613                 static_cast<uint32_t>(std::distance(first, last)), width, height, bufferSize });
614             last = first;
615             first = atlas.pending.begin();
616         }
617     }
618     if (!totalSize) {
619         return;
620     }
621 
622     // create a staging buffer big enough for all the columns
623     const GpuBufferDesc desc {
624         BufferUsageFlagBits::CORE_BUFFER_USAGE_TRANSFER_SRC_BIT, // usageFlags
625         MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
626             MemoryPropertyFlagBits::CORE_MEMORY_PROPERTY_HOST_COHERENT_BIT, // memoryPropertyFlags
627         EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_CREATE_IMMEDIATE |
628             EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_DEFERRED_DESTROY |
629             EngineBufferCreationFlagBits::CORE_ENGINE_BUFFER_CREATION_MAP_OUTSIDE_RENDERER, // engineCreationFlags
630         totalSize,                                                                          // byteSize
631         BASE_NS::Format::BASE_FORMAT_UNDEFINED,                                             // format
632     };
633     auto& gpuResMan = renderContext_.GetDevice().GetGpuResourceManager();
634     auto bufferHandle = gpuResMan.Create(desc);
635     auto* ptr = static_cast<uint8_t*>(gpuResMan.MapBufferMemory(bufferHandle));
636     if (!ptr) {
637         return;
638     }
639     auto buffer = array_view(ptr, totalSize);
640     std::fill(buffer.begin(), buffer.end(), uint8_t(0u));
641 
642     auto staging = refcnt_ptr<IRenderDataStoreDefaultStaging>(static_cast<IRenderDataStoreDefaultStaging*>(
643         renderContext_.GetRenderDataStoreManager().GetRenderDataStore(RENDER_DATA_STORE_DEFAULT_STAGING).get()));
644 
645     auto allColumnWidthsIter = allColumnWidths.cbegin();
646     uint32_t columnOffset = 0U;
647     for (auto& atlas : atlasTextures_) {
648         const auto bytesPerPixel = (atlas.inColor ? 4u : 1u);
649         for (auto& columnWidths : *allColumnWidthsIter) {
650             // copy each glyph in this column into the staging buffer
651             auto first = atlas.pending.begin() + columnWidths.index;
652             auto last = first + columnWidths.count;
653             std::for_each(first, last,
654                 [buffer = buffer.data() + columnOffset, width = columnWidths.width, bytesPerPixel,
655 
656                     firstPosY = first->posY](const PendingGlyph& glyph) mutable {
657                     if (glyph.width == width) {
658                         std::copy(glyph.data.begin().ptr(), glyph.data.end().ptr(),
659                             buffer + (width * bytesPerPixel * (glyph.posY - firstPosY)));
660                     } else {
661                         auto src = glyph.data.data();
662                         auto dst = buffer + (width * bytesPerPixel * (glyph.posY - firstPosY));
663                         const auto srcPitch = glyph.width * bytesPerPixel;
664                         const auto dstPitch = width * bytesPerPixel;
665                         for (auto i = 0u; i < glyph.height; ++i) {
666                             auto srcFirst = src + (i * srcPitch);
667                             auto srcLast = src + ((i + 1) * srcPitch);
668                             std::copy(srcFirst, srcLast, dst + (i * dstPitch));
669                         }
670                     }
671                 });
672 
673             // request to copy the column into the atlas texture
674             const BufferImageCopy bufferImageCopy {
675                 columnOffset,        // bufferOffset
676                 columnWidths.width,  // bufferRowLength
677                 columnWidths.height, // bufferImageHeight
678                 {
679                     CORE_IMAGE_ASPECT_COLOR_BIT,               // imageAspectFlags
680                     0,                                         // mipLevel
681                     0,                                         // baseArrayLayer
682                     1u,                                        // layerCount
683                 },                                             // imageSubresource
684                 { first->posX, first->posY, 0 },               // imageOffset
685                 { columnWidths.width, columnWidths.height, 1 } // imageExtend
686             };
687             staging->CopyBufferToImage(bufferHandle, atlas.handle, bufferImageCopy);
688 
689             columnOffset += columnWidths.byteSize;
690         }
691         ++allColumnWidthsIter;
692         atlas.pending.clear();
693     }
694 
695     gpuResMan.UnmapBuffer(bufferHandle);
696 }
697 
AddGlyphToColumn(Glyph & glyph,size_t atlasIndex,ColumnHeader & hdr,const FT_Bitmap & bitmap,uint32_t columnX)698 void FontManager::AddGlyphToColumn(
699     Glyph& glyph, size_t atlasIndex, ColumnHeader& hdr, const FT_Bitmap& bitmap, uint32_t columnX)
700 {
701     uint32_t width = bitmap.width;
702     uint32_t height = bitmap.rows;
703 
704     uint32_t x = columnX + BORDER_WIDTH;
705     uint32_t y = ATLAS_SIZE - hdr.heightLeft + BORDER_WIDTH;
706 
707     hdr.heightLeft -= static_cast<uint16_t>(height + BORDER_WIDTH_X2);
708 
709     glyph.atlas.index = static_cast<uint16_t>(atlasIndex);
710     glyph.atlas.rect.x = static_cast<uint16_t>(x);
711     glyph.atlas.rect.y = static_cast<uint16_t>(y);
712     glyph.atlas.rect.w = static_cast<uint16_t>(width);
713     glyph.atlas.rect.h = static_cast<uint16_t>(height);
714 
715     // Access locked by caller
716     uint32_t bpp = (atlasTextures_[atlasIndex].inColor ? 4u : 1u);
717 
718     if (width > 0 && height > 0 && bitmap.pitch / bpp >= width) {
719         atlasTextures_[atlasIndex].pending.push_back({
720             static_cast<uint16_t>(x),
721             static_cast<uint16_t>(y),
722             static_cast<uint16_t>(width),
723             static_cast<uint16_t>(height),
724             { bitmap.buffer, bitmap.buffer + bitmap.pitch * bitmap.rows },
725         });
726     }
727 }
728 
Gc(uint64_t uid,FontBuffer * data)729 void FontManager::Gc(uint64_t uid, FontBuffer* data)
730 {
731     // Check if file data cache has this font
732     std::lock_guard writerLock(fontBuffersMutex_);
733 
734     if (!fontBuffers_.erase(uid)) {
735         for (auto it = fontBuffers_.cbegin(); it != fontBuffers_.cend();) {
736             if (it->second.expired()) {
737                 fontBuffers_.erase(it);
738             } else {
739                 ++it;
740             }
741         }
742     }
743     if (fontBuffers_.empty()) {
744         FlushCaches();
745     }
746 }
747 
GetInterface(const BASE_NS::Uid & uid) const748 const CORE_NS::IInterface* FontManager::GetInterface(const BASE_NS::Uid& uid) const
749 {
750     if (uid == CORE_NS::IInterface::UID) {
751         return this;
752     }
753     if (uid == IFontManager::UID) {
754         return this;
755     }
756     return nullptr;
757 }
758 
GetInterface(const BASE_NS::Uid & uid)759 CORE_NS::IInterface* FontManager::GetInterface(const BASE_NS::Uid& uid)
760 {
761     if (uid == CORE_NS::IInterface::UID) {
762         return this;
763     }
764     if (uid == IFontManager::UID) {
765         return this;
766     }
767     return nullptr;
768 }
769 
Ref()770 void FontManager::Ref()
771 {
772     refcnt_.fetch_add(1, std::memory_order_relaxed);
773 }
774 
Unref()775 void FontManager::Unref()
776 {
777     if (refcnt_.fetch_sub(1, std::memory_order_release) == 1) {
778         std::atomic_thread_fence(std::memory_order_acquire);
779         CORE_LOG_N("delete FontManager");
780         delete this;
781     }
782 }
783 FONT_END_NAMESPACE()
784