• 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 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;
GetTypeFace(BASE_NS::string_view name)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     void UploadPending() override;
65 
66     // IInterface
67     const CORE_NS::IInterface* GetInterface(const BASE_NS::Uid& uid) const override;
68     CORE_NS::IInterface* GetInterface(const BASE_NS::Uid& uid) override;
69     void Ref() override;
70     void Unref() override;
71 
72     void Gc(uint64_t uid, FontBuffer* data);
73 
74     bool IsFont(BASE_NS::string_view uri);
75 
76     FT_Face OpenFtFace(BASE_NS::array_view<const uint8_t> buf, FT_Long index);
77 
78     // Called by FontData to request atlas slots
79     /*
80      * fit glyph to atlas
81      *
82      * @ret: -1 failure; 0 success; 1 atlas reset
83      *
84      */
85     int UpdateAtlas(FontDefs::Glyph& glyph, const FT_Bitmap&, bool inColor);
86 
87     /* atlas partitioning:
88      *
89      *  | 10 px | 24 px | 12 px | NN px | (sizes are for example)
90      *  +-------+-------+-------+-------+
91      *  | A     | a     | F     | E     | <== row 0
92      *  | a     | C     | f     | D     | <== row 1
93      *    ...     ...     ...     ...     <== row N
94      *    ...     ...     ...     ...
95      *    ...     ...     ...     ...
96      *
97      * Atlas info contains info on free width left and list of columns which have info on their width and free height
98      * left for that column.
99      *
100      * Filling algorithm works like this:
101      *  - checks if the color formats match (mono vs color font) and if not skips that atlas
102      *  - goes through all columns looking for an exact width match with enough free height left and uses that if found
103      *  - simultaneously keeps track of the closest width column that has enough free height left
104      *  - if no exact fit is found, checks if the closes width fit is close enough (GLYPH_FIT_THRESHOLD pixels) and uses
105      *    that
106      *  - if no close enough match is found, and atlas has enough free width left, creates a new column and puts the
107      *    glyph as first element there
108      *  - if none of the above conditions are fullfilled, creates a new atlas texture and creates a new column and puts
109      *    the glyph there
110      *
111      * In pseudo code:
112      * for ( atlas in atlases ) {
113      *     if ( atlas format is glyph format ) {
114      *         for ( column in atlas columns ) {
115      *             if ( column has glyph height free and is wider or equal to glyph width ) {
116      *                 if ( column width exactly glyph width ) {
117      *                     insert glyph to this column and return
118      *                 } else {
119      *                     update closest column width
120      *                 }
121      *             }
122      *         }
123      *         if ( closest column width is at most GLYPH_FIT_THRESHOLD pixels ) {
124      *                  insert glyph to closest width column and return
125      *         }
126      *         if ( atlas has glyph width free left ) {
127      *             allocate new column, insert the glyph and return
128      *         }
129      *     }
130      *  }
131      *  allocate new atlas, allocate first column, insert glyph and return
132      *
133      *  Initially there are no atlases available so the first glyph creates one as it falls to the end right away. Same
134      * happens when color format changes for the first time.
135      */
136     struct ColumnHeader {
137         uint16_t colWidth;
138         uint16_t heightLeft;
139     };
140     struct PendingGlyph {
141         uint16_t posX;
142         uint16_t posY;
143         uint16_t width;
144         uint16_t height;
145         BASE_NS::vector<uint8_t> data;
146     };
147     struct AtlasTexture {
148         RENDER_NS::RenderHandleReference handle;
149         BASE_NS::vector<ColumnHeader> columns;
150         BASE_NS::vector<PendingGlyph> pending;
151         uint32_t widthLeft;
152         bool inColor;
153 #if defined(FONT_VALIDATION_ENABLED) && (FONT_VALIDATION_ENABLED)
154         BASE_NS::string name;
155 #endif
156     };
157 
GetAtlas(size_t index)158     AtlasTexture* GetAtlas(size_t index)
159     {
160         std::shared_lock readerLock(atlasMutex_);
161 
162         if (index >= atlasTextures_.size()) {
163             return nullptr;
164         }
165         return &atlasTextures_[index];
166     }
167 
168 private:
169     bool OpenFtFace(BASE_NS::string_view uri, FT_Long index, FT_Face* face);
170 
171     void GetTypeFacesByFile(BASE_NS::vector<TypeFace>& typeFaces, BASE_NS::string_view path);
172     void GetTypeFacesByDir(BASE_NS::vector<TypeFace>& typeFaces, BASE_NS::string_view path);
173 
174     std::shared_ptr<FontBuffer> CreateFontBuffer(const TypeFace& typeFace);
175 
176     // Atlas manager
177     AtlasTexture* CreateAtlasTexture(bool color);
178     void AddGlyphToColumn(
179         FontDefs::Glyph& glyph, size_t atlasIndex, ColumnHeader& hdr, const FT_Bitmap& bitmap, uint32_t columnX);
180 
181     std::atomic_uint32_t refcnt_ { 0 };
182 
183     RENDER_NS::IRenderContext& renderContext_;
184 
185     FT_Library fontLib_;
186 
187     std::shared_mutex fontBuffersMutex_; // mutex for file data cache access
188     BASE_NS::unordered_map<uint64_t, std::weak_ptr<FontBuffer>> fontBuffers_;
189     BASE_NS::vector<TypeFace> typeFaces_;
190 
191     std::shared_mutex atlasMutex_; // mutex for atlas texture access
192     BASE_NS::vector<AtlasTexture> atlasTextures_;
193 };
194 
195 FONT_END_NAMESPACE()
196 
197 #endif // UTIL__FONT_MANAGER_H
198