1/*
2 * Copyright 2008 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkFontConfigInterface.h"
9#include "SkFontConfigTypeface.h"
10#include "SkFontDescriptor.h"
11#include "SkStream.h"
12#include "SkTemplates.h"
13#include "SkTypeface.h"
14#include "SkTypefaceCache.h"
15#include "SkResourceCache.h"
16
17///////////////////////////////////////////////////////////////////////////////
18///////////////////////////////////////////////////////////////////////////////
19
20SK_DECLARE_STATIC_MUTEX(gFontConfigInterfaceMutex);
21static SkFontConfigInterface* gFontConfigInterface;
22
23SkFontConfigInterface* SkFontConfigInterface::RefGlobal() {
24    SkAutoMutexAcquire ac(gFontConfigInterfaceMutex);
25
26    return SkSafeRef(gFontConfigInterface);
27}
28
29SkFontConfigInterface* SkFontConfigInterface::SetGlobal(SkFontConfigInterface* fc) {
30    SkAutoMutexAcquire ac(gFontConfigInterfaceMutex);
31
32    SkRefCnt_SafeAssign(gFontConfigInterface, fc);
33    return fc;
34}
35
36///////////////////////////////////////////////////////////////////////////////
37///////////////////////////////////////////////////////////////////////////////
38
39// convenience function to create the direct interface if none is installed.
40extern SkFontConfigInterface* SkCreateDirectFontConfigInterface();
41
42static SkFontConfigInterface* RefFCI() {
43    for (;;) {
44        SkFontConfigInterface* fci = SkFontConfigInterface::RefGlobal();
45        if (fci) {
46            return fci;
47        }
48        fci = SkFontConfigInterface::GetSingletonDirectInterface(&gFontConfigInterfaceMutex);
49        SkFontConfigInterface::SetGlobal(fci);
50    }
51}
52
53// export this to SkFontMgr_fontconfig.cpp until this file just goes away.
54SkFontConfigInterface* SkFontHost_fontconfig_ref_global();
55SkFontConfigInterface* SkFontHost_fontconfig_ref_global() {
56    return RefFCI();
57}
58
59///////////////////////////////////////////////////////////////////////////////
60
61static bool find_by_FontIdentity(SkTypeface* cachedTypeface, const SkFontStyle&, void* ctx) {
62    typedef SkFontConfigInterface::FontIdentity FontIdentity;
63    FontConfigTypeface* cachedFCTypeface = static_cast<FontConfigTypeface*>(cachedTypeface);
64    FontIdentity* identity = static_cast<FontIdentity*>(ctx);
65
66    return cachedFCTypeface->getIdentity() == *identity;
67}
68
69SK_DECLARE_STATIC_MUTEX(gSkFontHostRequestCacheMutex);
70class SkFontHostRequestCache {
71
72    // The value of maxSize here is a compromise between cache hits and cache size.
73    static const size_t gMaxSize = 1 << 12;
74
75    static SkFontHostRequestCache& Get() {
76        gSkFontHostRequestCacheMutex.assertHeld();
77        static SkFontHostRequestCache gCache(gMaxSize);
78        return gCache;
79    }
80
81public:
82    struct Request : public SkResourceCache::Key {
83    private:
84        Request(const char* name, size_t nameLen, const SkFontStyle& style) : fStyle(style) {
85            /** Pointer to just after the last field of this class. */
86            char* content = const_cast<char*>(SkTAfter<const char>(&this->fStyle));
87
88            // No holes.
89            SkASSERT(SkTAddOffset<char>(this, sizeof(SkResourceCache::Key) + keySize) == content);
90
91            // Has a size divisible by size of uint32_t.
92            SkASSERT((content - reinterpret_cast<char*>(this)) % sizeof(uint32_t) == 0);
93
94            size_t contentLen = SkAlign4(nameLen);
95            sk_careful_memcpy(content, name, nameLen);
96            sk_bzero(content + nameLen, contentLen - nameLen);
97            this->init(&gSkFontHostRequestCacheMutex, 0, keySize + contentLen);
98        }
99        const SkFontStyle fStyle;
100        /** The sum of the sizes of the fields of this class. */
101        static const size_t keySize = sizeof(fStyle);
102
103    public:
104        static Request* Create(const char* name, const SkFontStyle& style) {
105            size_t nameLen = name ? strlen(name) : 0;
106            size_t contentLen = SkAlign4(nameLen);
107            char* storage = new char[sizeof(Request) + contentLen];
108            return new (storage) Request(name, nameLen, style);
109        }
110        void operator delete(void* storage) {
111            delete[] reinterpret_cast<char*>(storage);
112        }
113    };
114
115
116private:
117    struct Result : public SkResourceCache::Rec {
118        Result(Request* request, SkTypeface* typeface)
119            : fRequest(request)
120            , fFace(SkSafeRef(typeface)) {}
121        Result(Result&&) = default;
122        Result& operator=(Result&&) = default;
123
124        const Key& getKey() const override { return *fRequest; }
125        size_t bytesUsed() const override { return fRequest->size() + sizeof(fFace); }
126        const char* getCategory() const override { return "request_cache"; }
127        SkDiscardableMemory* diagnostic_only_getDiscardable() const override { return nullptr; }
128
129        SkAutoTDelete<Request> fRequest;
130        SkAutoTUnref<SkTypeface> fFace;
131    };
132
133    SkResourceCache fCachedResults;
134
135public:
136    SkFontHostRequestCache(size_t maxSize) : fCachedResults(maxSize) {}
137
138    /** Takes ownership of request. It will be deleted when no longer needed. */
139    void add(SkTypeface* face, Request* request) {
140        fCachedResults.add(new Result(request, face));
141    }
142    /** Does not take ownership of request. */
143    SkTypeface* findAndRef(Request* request) {
144        SkTypeface* face = nullptr;
145        fCachedResults.find(*request, [](const SkResourceCache::Rec& rec, void* context) -> bool {
146            const Result& result = static_cast<const Result&>(rec);
147            SkTypeface** face = static_cast<SkTypeface**>(context);
148
149            *face = result.fFace;
150            return true;
151        }, &face);
152        return SkSafeRef(face);
153    }
154
155    /** Takes ownership of request. It will be deleted when no longer needed. */
156    static void Add(SkTypeface* face, Request* request) {
157        SkAutoMutexAcquire ama(gSkFontHostRequestCacheMutex);
158        Get().add(face, request);
159    }
160
161    /** Does not take ownership of request. */
162    static SkTypeface* FindAndRef(Request* request) {
163        SkAutoMutexAcquire ama(gSkFontHostRequestCacheMutex);
164        return Get().findAndRef(request);
165    }
166};
167
168SkTypeface* FontConfigTypeface::LegacyCreateTypeface(const char requestedFamilyName[],
169                                                     SkTypeface::Style requestedOldStyle)
170{
171    SkAutoTUnref<SkFontConfigInterface> fci(RefFCI());
172    if (nullptr == fci.get()) {
173        return nullptr;
174    }
175
176    // Check if this request is already in the request cache.
177    using Request = SkFontHostRequestCache::Request;
178    SkFontStyle requestedStyle(requestedOldStyle);
179    SkAutoTDelete<Request> request(Request::Create(requestedFamilyName, requestedStyle));
180    SkTypeface* face = SkFontHostRequestCache::FindAndRef(request);
181    if (face) {
182        return face;
183    }
184
185    SkFontConfigInterface::FontIdentity identity;
186    SkString outFamilyName;
187    SkTypeface::Style outOldStyle;
188    if (!fci->matchFamilyName(requestedFamilyName, requestedOldStyle,
189                              &identity, &outFamilyName, &outOldStyle))
190    {
191        return nullptr;
192    }
193
194    // Check if a typeface with this FontIdentity is already in the FontIdentity cache.
195    face = SkTypefaceCache::FindByProcAndRef(find_by_FontIdentity, &identity);
196    if (!face) {
197        face = FontConfigTypeface::Create(SkFontStyle(outOldStyle), identity, outFamilyName);
198        // Add this FontIdentity to the FontIdentity cache.
199        SkTypefaceCache::Add(face, SkFontStyle(outOldStyle));
200    }
201    // Add this request to the request cache.
202    SkFontHostRequestCache::Add(face, request.release());
203
204    return face;
205}
206
207///////////////////////////////////////////////////////////////////////////////
208
209SkStreamAsset* FontConfigTypeface::onOpenStream(int* ttcIndex) const {
210    SkStreamAsset* stream = this->getLocalStream();
211    if (stream) {
212        // TODO: should have been provided by CreateFromStream()
213        *ttcIndex = 0;
214        return stream->duplicate();
215    }
216
217    SkAutoTUnref<SkFontConfigInterface> fci(RefFCI());
218    if (nullptr == fci.get()) {
219        return nullptr;
220    }
221
222    *ttcIndex = this->getIdentity().fTTCIndex;
223    return fci->openStream(this->getIdentity());
224}
225
226void FontConfigTypeface::onGetFamilyName(SkString* familyName) const {
227    *familyName = fFamilyName;
228}
229
230void FontConfigTypeface::onGetFontDescriptor(SkFontDescriptor* desc,
231                                             bool* isLocalStream) const {
232    SkString name;
233    this->getFamilyName(&name);
234    desc->setFamilyName(name.c_str());
235    *isLocalStream = SkToBool(this->getLocalStream());
236}
237