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 <cutils/log.h>
22#include <hb.h>
23#include <hb-ot.h>
24#include <utils/LruCache.h>
25
26#include <minikin/MinikinFont.h>
27#include "MinikinInternal.h"
28
29namespace android {
30
31static hb_blob_t* referenceTable(hb_face_t* /* face */, hb_tag_t tag, void* userData) {
32    MinikinFont* font = reinterpret_cast<MinikinFont*>(userData);
33    MinikinDestroyFunc destroy = 0;
34    size_t size = 0;
35    const void* buffer = font->GetTable(tag, &size, &destroy);
36    if (buffer == nullptr) {
37        return nullptr;
38    }
39#ifdef VERBOSE_DEBUG
40    ALOGD("referenceTable %c%c%c%c length=%zd",
41        (tag >>24)&0xff, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff, size);
42#endif
43    return hb_blob_create(reinterpret_cast<const char*>(buffer), size,
44            HB_MEMORY_MODE_READONLY, const_cast<void*>(buffer), destroy);
45}
46
47class HbFontCache : private OnEntryRemoved<int32_t, hb_font_t*> {
48public:
49    HbFontCache() : mCache(kMaxEntries) {
50        mCache.setOnEntryRemovedListener(this);
51    }
52
53    // callback for OnEntryRemoved
54    void operator()(int32_t& /* key */, hb_font_t*& value) {
55        hb_font_destroy(value);
56    }
57
58    hb_font_t* get(int32_t fontId) {
59        return mCache.get(fontId);
60    }
61
62    void put(int32_t fontId, hb_font_t* font) {
63        mCache.put(fontId, font);
64    }
65
66    void clear() {
67        mCache.clear();
68    }
69
70    void remove(int32_t fontId) {
71        mCache.remove(fontId);
72    }
73
74private:
75    static const size_t kMaxEntries = 100;
76
77    LruCache<int32_t, hb_font_t*> mCache;
78};
79
80HbFontCache* getFontCacheLocked() {
81    assertMinikinLocked();
82    static HbFontCache* cache = nullptr;
83    if (cache == nullptr) {
84        cache = new HbFontCache();
85    }
86    return cache;
87}
88
89void purgeHbFontCacheLocked() {
90    assertMinikinLocked();
91    getFontCacheLocked()->clear();
92}
93
94void purgeHbFontLocked(const MinikinFont* minikinFont) {
95    assertMinikinLocked();
96    const int32_t fontId = minikinFont->GetUniqueId();
97    getFontCacheLocked()->remove(fontId);
98}
99
100// Returns a new reference to a hb_font_t object, caller is
101// responsible for calling hb_font_destroy() on it.
102hb_font_t* getHbFontLocked(MinikinFont* minikinFont) {
103    assertMinikinLocked();
104    // TODO: get rid of nullFaceFont
105    static hb_font_t* nullFaceFont = nullptr;
106    if (minikinFont == nullptr) {
107        if (nullFaceFont == nullptr) {
108            nullFaceFont = hb_font_create(nullptr);
109        }
110        return hb_font_reference(nullFaceFont);
111    }
112
113    HbFontCache* fontCache = getFontCacheLocked();
114    const int32_t fontId = minikinFont->GetUniqueId();
115    hb_font_t* font = fontCache->get(fontId);
116    if (font != nullptr) {
117        return hb_font_reference(font);
118    }
119
120    hb_face_t* face;
121    const void* buf = minikinFont->GetFontData();
122    if (buf == nullptr) {
123        face = hb_face_create_for_tables(referenceTable, minikinFont, nullptr);
124    } else {
125        size_t size = minikinFont->GetFontSize();
126        hb_blob_t* blob = hb_blob_create(reinterpret_cast<const char*>(buf), size,
127            HB_MEMORY_MODE_READONLY, nullptr, nullptr);
128        face = hb_face_create(blob, minikinFont->GetFontIndex());
129        hb_blob_destroy(blob);
130    }
131    hb_font_t* parent_font = hb_font_create(face);
132    hb_ot_font_set_funcs(parent_font);
133
134    unsigned int upem = hb_face_get_upem(face);
135    hb_font_set_scale(parent_font, upem, upem);
136
137    font = hb_font_create_sub_font(parent_font);
138    hb_font_destroy(parent_font);
139    hb_face_destroy(face);
140    fontCache->put(fontId, font);
141    return hb_font_reference(font);
142}
143
144}  // namespace android
145