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