18bf80f4bSopenharmony_ci/*
28bf80f4bSopenharmony_ci * Copyright (c) 2024 Huawei Device Co., Ltd.
38bf80f4bSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
48bf80f4bSopenharmony_ci * you may not use this file except in compliance with the License.
58bf80f4bSopenharmony_ci * You may obtain a copy of the License at
68bf80f4bSopenharmony_ci *
78bf80f4bSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
88bf80f4bSopenharmony_ci *
98bf80f4bSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
108bf80f4bSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
118bf80f4bSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128bf80f4bSopenharmony_ci * See the License for the specific language governing permissions and
138bf80f4bSopenharmony_ci * limitations under the License.
148bf80f4bSopenharmony_ci */
158bf80f4bSopenharmony_ci
168bf80f4bSopenharmony_ci#ifndef UTIL__FONT_MANAGER_H
178bf80f4bSopenharmony_ci#define UTIL__FONT_MANAGER_H
188bf80f4bSopenharmony_ci
198bf80f4bSopenharmony_ci#include <atomic>
208bf80f4bSopenharmony_ci#include <ft2build.h>
218bf80f4bSopenharmony_ci#include <memory>
228bf80f4bSopenharmony_ci#include <shared_mutex>
238bf80f4bSopenharmony_ci#include FT_FREETYPE_H
248bf80f4bSopenharmony_ci#include <freetype/ftglyph.h>
258bf80f4bSopenharmony_ci
268bf80f4bSopenharmony_ci#include <base/containers/unordered_map.h>
278bf80f4bSopenharmony_ci#include <font/intf_font_manager.h>
288bf80f4bSopenharmony_ci#include <render/resource_handle.h>
298bf80f4bSopenharmony_ci
308bf80f4bSopenharmony_ci#include "font_defs.h"
318bf80f4bSopenharmony_ci
328bf80f4bSopenharmony_ciRENDER_BEGIN_NAMESPACE()
338bf80f4bSopenharmony_ciclass IRenderContext;
348bf80f4bSopenharmony_ciRENDER_END_NAMESPACE()
358bf80f4bSopenharmony_ci
368bf80f4bSopenharmony_ciFONT_BEGIN_NAMESPACE()
378bf80f4bSopenharmony_ciclass FontBuffer;
388bf80f4bSopenharmony_ci
398bf80f4bSopenharmony_ciclass FontManager final : public IFontManager {
408bf80f4bSopenharmony_cipublic:
418bf80f4bSopenharmony_ci    explicit FontManager(RENDER_NS::IRenderContext& context);
428bf80f4bSopenharmony_ci    ~FontManager();
438bf80f4bSopenharmony_ci
448bf80f4bSopenharmony_ci    FontManager(FontManager const&) = delete;
458bf80f4bSopenharmony_ci    FontManager& operator=(FontManager const&) = delete;
468bf80f4bSopenharmony_ci
478bf80f4bSopenharmony_ci    // IFontManager
488bf80f4bSopenharmony_ci    uint32_t GetGlyphIndex(const TypeFace&, uint32_t code) override;
498bf80f4bSopenharmony_ci
508bf80f4bSopenharmony_ci    BASE_NS::array_view<const TypeFace> GetTypeFaces() const override;
518bf80f4bSopenharmony_ci    inline const TypeFace* GetTypeFace(BASE_NS::string_view name) override
528bf80f4bSopenharmony_ci    {
538bf80f4bSopenharmony_ci        return GetTypeFace(name, nullptr);
548bf80f4bSopenharmony_ci    }
558bf80f4bSopenharmony_ci    const TypeFace* GetTypeFace(BASE_NS::string_view name, BASE_NS::string_view styleName) override;
568bf80f4bSopenharmony_ci    BASE_NS::vector<TypeFace> GetTypeFaces(BASE_NS::string_view filePath) override;
578bf80f4bSopenharmony_ci    BASE_NS::vector<TypeFace> GetTypeFaces(BASE_NS::array_view<const BASE_NS::string_view> lookupDirs) override;
588bf80f4bSopenharmony_ci
598bf80f4bSopenharmony_ci    IFont::Ptr CreateFont(const TypeFace& typeface) override;
608bf80f4bSopenharmony_ci    IFont::Ptr CreateFontFromMemory(const TypeFace& typeface, const BASE_NS::vector<uint8_t>& fontData) override;
618bf80f4bSopenharmony_ci
628bf80f4bSopenharmony_ci    void FlushCaches() override;
638bf80f4bSopenharmony_ci
648bf80f4bSopenharmony_ci    // IInterface
658bf80f4bSopenharmony_ci    const CORE_NS::IInterface* GetInterface(const BASE_NS::Uid& uid) const override;
668bf80f4bSopenharmony_ci    CORE_NS::IInterface* GetInterface(const BASE_NS::Uid& uid) override;
678bf80f4bSopenharmony_ci    void Ref() override;
688bf80f4bSopenharmony_ci    void Unref() override;
698bf80f4bSopenharmony_ci
708bf80f4bSopenharmony_ci    void Gc(uint64_t uid, FontBuffer* data);
718bf80f4bSopenharmony_ci
728bf80f4bSopenharmony_ci    FT_Face OpenFtFace(BASE_NS::array_view<const uint8_t> buf, FT_Long index);
738bf80f4bSopenharmony_ci
748bf80f4bSopenharmony_ci    // Called by FontData to request atlas slots
758bf80f4bSopenharmony_ci    /*
768bf80f4bSopenharmony_ci     * fit glyph to atlas
778bf80f4bSopenharmony_ci     *
788bf80f4bSopenharmony_ci     * @ret: -1 failure; 0 success; 1 atlas reset
798bf80f4bSopenharmony_ci     *
808bf80f4bSopenharmony_ci     */
818bf80f4bSopenharmony_ci    int UpdateAtlas(FontDefs::Glyph& glyph, const FT_Bitmap&, bool inColor);
828bf80f4bSopenharmony_ci
838bf80f4bSopenharmony_ci    void UploadPending();
848bf80f4bSopenharmony_ci
858bf80f4bSopenharmony_ci    /* atlas partitioning:
868bf80f4bSopenharmony_ci     *
878bf80f4bSopenharmony_ci     *  | 10 px | 24 px | 12 px | NN px | (sizes are for example)
888bf80f4bSopenharmony_ci     *  +-------+-------+-------+-------+
898bf80f4bSopenharmony_ci     *  | A     | a     | F     | E     | <== row 0
908bf80f4bSopenharmony_ci     *  | a     | C     | f     | D     | <== row 1
918bf80f4bSopenharmony_ci     *    ...     ...     ...     ...     <== row N
928bf80f4bSopenharmony_ci     *    ...     ...     ...     ...
938bf80f4bSopenharmony_ci     *    ...     ...     ...     ...
948bf80f4bSopenharmony_ci     *
958bf80f4bSopenharmony_ci     * Atlas info contains info on free width left and list of columns which have info on their width and free height
968bf80f4bSopenharmony_ci     * left for that column.
978bf80f4bSopenharmony_ci     *
988bf80f4bSopenharmony_ci     * Filling algorithm works like this:
998bf80f4bSopenharmony_ci     *  - checks if the color formats match (mono vs color font) and if not skips that atlas
1008bf80f4bSopenharmony_ci     *  - goes through all columns looking for an exact width match with enough free height left and uses that if found
1018bf80f4bSopenharmony_ci     *  - simultaneously keeps track of the closest width column that has enough free height left
1028bf80f4bSopenharmony_ci     *  - if no exact fit is found, checks if the closes width fit is close enough (GLYPH_FIT_THRESHOLD pixels) and uses
1038bf80f4bSopenharmony_ci     *    that
1048bf80f4bSopenharmony_ci     *  - if no close enough match is found, and atlas has enough free width left, creates a new column and puts the
1058bf80f4bSopenharmony_ci     *    glyph as first element there
1068bf80f4bSopenharmony_ci     *  - if none of the above conditions are fullfilled, creates a new atlas texture and creates a new column and puts
1078bf80f4bSopenharmony_ci     *    the glyph there
1088bf80f4bSopenharmony_ci     *
1098bf80f4bSopenharmony_ci     * In pseudo code:
1108bf80f4bSopenharmony_ci     * for ( atlas in atlases ) {
1118bf80f4bSopenharmony_ci     *     if ( atlas format is glyph format ) {
1128bf80f4bSopenharmony_ci     *         for ( column in atlas columns ) {
1138bf80f4bSopenharmony_ci     *             if ( column has glyph height free and is wider or equal to glyph width ) {
1148bf80f4bSopenharmony_ci     *                 if ( column width exactly glyph width ) {
1158bf80f4bSopenharmony_ci     *                     insert glyph to this column and return
1168bf80f4bSopenharmony_ci     *                 } else {
1178bf80f4bSopenharmony_ci     *                     update closest column width
1188bf80f4bSopenharmony_ci     *                 }
1198bf80f4bSopenharmony_ci     *             }
1208bf80f4bSopenharmony_ci     *         }
1218bf80f4bSopenharmony_ci     *         if ( closest column width is at most GLYPH_FIT_THRESHOLD pixels ) {
1228bf80f4bSopenharmony_ci     *                  insert glyph to closest width column and return
1238bf80f4bSopenharmony_ci     *         }
1248bf80f4bSopenharmony_ci     *         if ( atlas has glyph width free left ) {
1258bf80f4bSopenharmony_ci     *             allocate new column, insert the glyph and return
1268bf80f4bSopenharmony_ci     *         }
1278bf80f4bSopenharmony_ci     *     }
1288bf80f4bSopenharmony_ci     *  }
1298bf80f4bSopenharmony_ci     *  allocate new atlas, allocate first column, insert glyph and return
1308bf80f4bSopenharmony_ci     *
1318bf80f4bSopenharmony_ci     *  Initially there are no atlases available so the first glyph creates one as it falls to the end right away. Same
1328bf80f4bSopenharmony_ci     * happens when color format changes for the first time.
1338bf80f4bSopenharmony_ci     */
1348bf80f4bSopenharmony_ci    struct ColumnHeader {
1358bf80f4bSopenharmony_ci        uint16_t colWidth;
1368bf80f4bSopenharmony_ci        uint16_t heightLeft;
1378bf80f4bSopenharmony_ci    };
1388bf80f4bSopenharmony_ci    struct PendingGlyph {
1398bf80f4bSopenharmony_ci        uint16_t posX;
1408bf80f4bSopenharmony_ci        uint16_t posY;
1418bf80f4bSopenharmony_ci        uint16_t width;
1428bf80f4bSopenharmony_ci        uint16_t height;
1438bf80f4bSopenharmony_ci        BASE_NS::vector<uint8_t> data;
1448bf80f4bSopenharmony_ci    };
1458bf80f4bSopenharmony_ci    struct AtlasTexture {
1468bf80f4bSopenharmony_ci        RENDER_NS::RenderHandleReference handle;
1478bf80f4bSopenharmony_ci        BASE_NS::vector<ColumnHeader> columns;
1488bf80f4bSopenharmony_ci        BASE_NS::vector<PendingGlyph> pending;
1498bf80f4bSopenharmony_ci        uint32_t widthLeft;
1508bf80f4bSopenharmony_ci        bool inColor;
1518bf80f4bSopenharmony_ci#if defined(FONT_VALIDATION_ENABLED) && (FONT_VALIDATION_ENABLED)
1528bf80f4bSopenharmony_ci        BASE_NS::string name;
1538bf80f4bSopenharmony_ci#endif
1548bf80f4bSopenharmony_ci    };
1558bf80f4bSopenharmony_ci
1568bf80f4bSopenharmony_ci    AtlasTexture* GetAtlas(size_t index)
1578bf80f4bSopenharmony_ci    {
1588bf80f4bSopenharmony_ci        std::shared_lock readerLock(atlasMutex_);
1598bf80f4bSopenharmony_ci
1608bf80f4bSopenharmony_ci        if (index >= atlasTextures_.size()) {
1618bf80f4bSopenharmony_ci            return nullptr;
1628bf80f4bSopenharmony_ci        }
1638bf80f4bSopenharmony_ci        return &atlasTextures_[index];
1648bf80f4bSopenharmony_ci    }
1658bf80f4bSopenharmony_ci
1668bf80f4bSopenharmony_ciprivate:
1678bf80f4bSopenharmony_ci    void GetTypeFacesByFile(BASE_NS::vector<TypeFace>& typeFaces, BASE_NS::string_view path);
1688bf80f4bSopenharmony_ci    void GetTypeFacesByDir(BASE_NS::vector<TypeFace>& typeFaces, BASE_NS::string_view path);
1698bf80f4bSopenharmony_ci
1708bf80f4bSopenharmony_ci    std::shared_ptr<FontBuffer> CreateFontBuffer(const TypeFace& typeFace);
1718bf80f4bSopenharmony_ci
1728bf80f4bSopenharmony_ci    // Atlas manager
1738bf80f4bSopenharmony_ci    AtlasTexture* CreateAtlasTexture(bool color);
1748bf80f4bSopenharmony_ci    void AddGlyphToColumn(
1758bf80f4bSopenharmony_ci        FontDefs::Glyph& glyph, size_t atlasIndex, ColumnHeader& hdr, const FT_Bitmap& bitmap, uint32_t columnX);
1768bf80f4bSopenharmony_ci
1778bf80f4bSopenharmony_ci    std::atomic_uint32_t refcnt_ { 0 };
1788bf80f4bSopenharmony_ci
1798bf80f4bSopenharmony_ci    RENDER_NS::IRenderContext& renderContext_;
1808bf80f4bSopenharmony_ci
1818bf80f4bSopenharmony_ci    FT_Library fontLib_;
1828bf80f4bSopenharmony_ci
1838bf80f4bSopenharmony_ci    std::shared_mutex fontBuffersMutex_; // mutex for file data cache access
1848bf80f4bSopenharmony_ci    BASE_NS::unordered_map<uint64_t, std::weak_ptr<FontBuffer>> fontBuffers_;
1858bf80f4bSopenharmony_ci    BASE_NS::vector<TypeFace> typeFaces_;
1868bf80f4bSopenharmony_ci
1878bf80f4bSopenharmony_ci    std::shared_mutex atlasMutex_; // mutex for atlas texture access
1888bf80f4bSopenharmony_ci    BASE_NS::vector<AtlasTexture> atlasTextures_;
1898bf80f4bSopenharmony_ci};
1908bf80f4bSopenharmony_ci
1918bf80f4bSopenharmony_ciFONT_END_NAMESPACE()
1928bf80f4bSopenharmony_ci
1938bf80f4bSopenharmony_ci#endif // UTIL__FONT_MANAGER_H
194