1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "Minikin"
18
19#include "HbFontCache.h"
20
21#include <log/log.h>
22#include <utils/LruCache.h>
23
24#include <hb.h>
25#include <hb-ot.h>
26
27#include <minikin/MinikinFont.h>
28#include "MinikinInternal.h"
29
30namespace minikin {
31
32class HbFontCache : private android::OnEntryRemoved<int32_t, hb_font_t*> {
33public:
34    HbFontCache() : mCache(kMaxEntries) {
35        mCache.setOnEntryRemovedListener(this);
36    }
37
38    // callback for OnEntryRemoved
39    void operator()(int32_t& /* key */, hb_font_t*& value) {
40        hb_font_destroy(value);
41    }
42
43    hb_font_t* get(int32_t fontId) {
44        return mCache.get(fontId);
45    }
46
47    void put(int32_t fontId, hb_font_t* font) {
48        mCache.put(fontId, font);
49    }
50
51    void clear() {
52        mCache.clear();
53    }
54
55    void remove(int32_t fontId) {
56        mCache.remove(fontId);
57    }
58
59private:
60    static const size_t kMaxEntries = 100;
61
62    android::LruCache<int32_t, hb_font_t*> mCache;
63};
64
65HbFontCache* getFontCacheLocked() {
66    assertMinikinLocked();
67    static HbFontCache* cache = nullptr;
68    if (cache == nullptr) {
69        cache = new HbFontCache();
70    }
71    return cache;
72}
73
74void purgeHbFontCacheLocked() {
75    assertMinikinLocked();
76    getFontCacheLocked()->clear();
77}
78
79void purgeHbFontLocked(const MinikinFont* minikinFont) {
80    assertMinikinLocked();
81    const int32_t fontId = minikinFont->GetUniqueId();
82    getFontCacheLocked()->remove(fontId);
83}
84
85// Returns a new reference to a hb_font_t object, caller is
86// responsible for calling hb_font_destroy() on it.
87hb_font_t* getHbFontLocked(const MinikinFont* minikinFont) {
88    assertMinikinLocked();
89    // TODO: get rid of nullFaceFont
90    static hb_font_t* nullFaceFont = nullptr;
91    if (minikinFont == nullptr) {
92        if (nullFaceFont == nullptr) {
93            nullFaceFont = hb_font_create(nullptr);
94        }
95        return hb_font_reference(nullFaceFont);
96    }
97
98    HbFontCache* fontCache = getFontCacheLocked();
99    const int32_t fontId = minikinFont->GetUniqueId();
100    hb_font_t* font = fontCache->get(fontId);
101    if (font != nullptr) {
102        return hb_font_reference(font);
103    }
104
105    hb_face_t* face;
106    const void* buf = minikinFont->GetFontData();
107    size_t size = minikinFont->GetFontSize();
108    hb_blob_t* blob = hb_blob_create(reinterpret_cast<const char*>(buf), size,
109        HB_MEMORY_MODE_READONLY, nullptr, nullptr);
110    face = hb_face_create(blob, minikinFont->GetFontIndex());
111    hb_blob_destroy(blob);
112
113    hb_font_t* parent_font = hb_font_create(face);
114    hb_ot_font_set_funcs(parent_font);
115
116    unsigned int upem = hb_face_get_upem(face);
117    hb_font_set_scale(parent_font, upem, upem);
118
119    font = hb_font_create_sub_font(parent_font);
120    std::vector<hb_variation_t> variations;
121    for (const FontVariation& variation : minikinFont->GetAxes()) {
122        variations.push_back({variation.axisTag, variation.value});
123    }
124    hb_font_set_variations(font, variations.data(), variations.size());
125    hb_font_destroy(parent_font);
126    hb_face_destroy(face);
127    fontCache->put(fontId, font);
128    return hb_font_reference(font);
129}
130
131}  // namespace minikin
132