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 UTIL__FONT_MANAGER_H
17 #define UTIL__FONT_MANAGER_H
18 
19 #include <atomic>
20 #include <ft2build.h>
21 #include <memory>
22 #include <shared_mutex>
23 #include FT_FREETYPE_H
24 #include <freetype/ftglyph.h>
25 
26 #include <base/containers/unordered_map.h>
27 #include <font/intf_font_manager.h>
28 #include <render/resource_handle.h>
29 
30 #include "font_defs.h"
31 
32 RENDER_BEGIN_NAMESPACE()
33 class IRenderContext;
34 RENDER_END_NAMESPACE()
35 
36 FONT_BEGIN_NAMESPACE()
37 class FontBuffer;
38 
39 class FontManager final : public IFontManager {
40 public:
41     explicit FontManager(RENDER_NS::IRenderContext& context);
42     ~FontManager();
43 
44     FontManager(FontManager const&) = delete;
45     FontManager& operator=(FontManager const&) = delete;
46 
47     // IFontManager
48     uint32_t GetGlyphIndex(const TypeFace&, uint32_t code) override;
49 
50     BASE_NS::array_view<const TypeFace> GetTypeFaces() const override;
51     inline const TypeFace* GetTypeFace(BASE_NS::string_view name) override
52     {
53         return GetTypeFace(name, nullptr);
54     }
55     const TypeFace* GetTypeFace(BASE_NS::string_view name, BASE_NS::string_view styleName) override;
56     BASE_NS::vector<TypeFace> GetTypeFaces(BASE_NS::string_view filePath) override;
57     BASE_NS::vector<TypeFace> GetTypeFaces(BASE_NS::array_view<const BASE_NS::string_view> lookupDirs) override;
58 
59     IFont::Ptr CreateFont(const TypeFace& typeface) override;
60     IFont::Ptr CreateFontFromMemory(const TypeFace& typeface, const BASE_NS::vector<uint8_t>& fontData) override;
61 
62     void FlushCaches() override;
63 
64     // IInterface
65     const CORE_NS::IInterface* GetInterface(const BASE_NS::Uid& uid) const override;
66     CORE_NS::IInterface* GetInterface(const BASE_NS::Uid& uid) override;
67     void Ref() override;
68     void Unref() override;
69 
70     void Gc(uint64_t uid, FontBuffer* data);
71 
72     FT_Face OpenFtFace(BASE_NS::array_view<const uint8_t> buf, FT_Long index);
73 
74     // Called by FontData to request atlas slots
75     /*
76      * fit glyph to atlas
77      *
78      * @ret: -1 failure; 0 success; 1 atlas reset
79      *
80      */
81     int UpdateAtlas(FontDefs::Glyph& glyph, const FT_Bitmap&, bool inColor);
82 
83     void UploadPending();
84 
85     /* atlas partitioning:
86      *
87      *  | 10 px | 24 px | 12 px | NN px | (sizes are for example)
88      *  +-------+-------+-------+-------+
89      *  | A     | a     | F     | E     | <== row 0
90      *  | a     | C     | f     | D     | <== row 1
91      *    ...     ...     ...     ...     <== row N
92      *    ...     ...     ...     ...
93      *    ...     ...     ...     ...
94      *
95      * Atlas info contains info on free width left and list of columns which have info on their width and free height
96      * left for that column.
97      *
98      * Filling algorithm works like this:
99      *  - checks if the color formats match (mono vs color font) and if not skips that atlas
100      *  - goes through all columns looking for an exact width match with enough free height left and uses that if found
101      *  - simultaneously keeps track of the closest width column that has enough free height left
102      *  - if no exact fit is found, checks if the closes width fit is close enough (GLYPH_FIT_THRESHOLD pixels) and uses
103      *    that
104      *  - if no close enough match is found, and atlas has enough free width left, creates a new column and puts the
105      *    glyph as first element there
106      *  - if none of the above conditions are fullfilled, creates a new atlas texture and creates a new column and puts
107      *    the glyph there
108      *
109      * In pseudo code:
110      * for ( atlas in atlases ) {
111      *     if ( atlas format is glyph format ) {
112      *         for ( column in atlas columns ) {
113      *             if ( column has glyph height free and is wider or equal to glyph width ) {
114      *                 if ( column width exactly glyph width ) {
115      *                     insert glyph to this column and return
116      *                 } else {
117      *                     update closest column width
118      *                 }
119      *             }
120      *         }
121      *         if ( closest column width is at most GLYPH_FIT_THRESHOLD pixels ) {
122      *                  insert glyph to closest width column and return
123      *         }
124      *         if ( atlas has glyph width free left ) {
125      *             allocate new column, insert the glyph and return
126      *         }
127      *     }
128      *  }
129      *  allocate new atlas, allocate first column, insert glyph and return
130      *
131      *  Initially there are no atlases available so the first glyph creates one as it falls to the end right away. Same
132      * happens when color format changes for the first time.
133      */
134     struct ColumnHeader {
135         uint16_t colWidth;
136         uint16_t heightLeft;
137     };
138     struct PendingGlyph {
139         uint16_t posX;
140         uint16_t posY;
141         uint16_t width;
142         uint16_t height;
143         BASE_NS::vector<uint8_t> data;
144     };
145     struct AtlasTexture {
146         RENDER_NS::RenderHandleReference handle;
147         BASE_NS::vector<ColumnHeader> columns;
148         BASE_NS::vector<PendingGlyph> pending;
149         uint32_t widthLeft;
150         bool inColor;
151 #if defined(FONT_VALIDATION_ENABLED) && (FONT_VALIDATION_ENABLED)
152         BASE_NS::string name;
153 #endif
154     };
155 
GetAtlas(size_t index)156     AtlasTexture* GetAtlas(size_t index)
157     {
158         std::shared_lock readerLock(atlasMutex_);
159 
160         if (index >= atlasTextures_.size()) {
161             return nullptr;
162         }
163         return &atlasTextures_[index];
164     }
165 
166 private:
167     void GetTypeFacesByFile(BASE_NS::vector<TypeFace>& typeFaces, BASE_NS::string_view path);
168     void GetTypeFacesByDir(BASE_NS::vector<TypeFace>& typeFaces, BASE_NS::string_view path);
169 
170     std::shared_ptr<FontBuffer> CreateFontBuffer(const TypeFace& typeFace);
171 
172     // Atlas manager
173     AtlasTexture* CreateAtlasTexture(bool color);
174     void AddGlyphToColumn(
175         FontDefs::Glyph& glyph, size_t atlasIndex, ColumnHeader& hdr, const FT_Bitmap& bitmap, uint32_t columnX);
176 
177     std::atomic_uint32_t refcnt_ { 0 };
178 
179     RENDER_NS::IRenderContext& renderContext_;
180 
181     FT_Library fontLib_;
182 
183     std::shared_mutex fontBuffersMutex_; // mutex for file data cache access
184     BASE_NS::unordered_map<uint64_t, std::weak_ptr<FontBuffer>> fontBuffers_;
185     BASE_NS::vector<TypeFace> typeFaces_;
186 
187     std::shared_mutex atlasMutex_; // mutex for atlas texture access
188     BASE_NS::vector<AtlasTexture> atlasTextures_;
189 };
190 
191 FONT_END_NAMESPACE()
192 
193 #endif // UTIL__FONT_MANAGER_H
194