1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "NativeFontResourceDWrite.h"
8 #include "UnscaledFontDWrite.h"
9
10 #include <unordered_map>
11
12 #include "Logging.h"
13 #include "mozilla/RefPtr.h"
14 #include "mozilla/StaticMutex.h"
15 #include "nsTArray.h"
16
17 namespace mozilla {
18 namespace gfx {
19
20 static StaticMutex sFontFileStreamsMutex;
21 static uint64_t sNextFontFileKey = 0;
22 static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;
23
24 class DWriteFontFileLoader : public IDWriteFontFileLoader {
25 public:
DWriteFontFileLoader()26 DWriteFontFileLoader() {}
27
28 // IUnknown interface
IFACEMETHOD(QueryInterface)29 IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) {
30 if (iid == __uuidof(IDWriteFontFileLoader)) {
31 *ppObject = static_cast<IDWriteFontFileLoader*>(this);
32 return S_OK;
33 } else if (iid == __uuidof(IUnknown)) {
34 *ppObject = static_cast<IUnknown*>(this);
35 return S_OK;
36 } else {
37 return E_NOINTERFACE;
38 }
39 }
40
IFACEMETHOD_(ULONG,AddRef)41 IFACEMETHOD_(ULONG, AddRef)() { return 1; }
42
IFACEMETHOD_(ULONG,Release)43 IFACEMETHOD_(ULONG, Release)() { return 1; }
44
45 // IDWriteFontFileLoader methods
46 /**
47 * Important! Note the key here has to be a uint64_t that will have been
48 * generated by incrementing sNextFontFileKey.
49 */
50 virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(
51 void const* fontFileReferenceKey, UINT32 fontFileReferenceKeySize,
52 OUT IDWriteFontFileStream** fontFileStream);
53
54 /**
55 * Gets the singleton loader instance. Note that when using this font
56 * loader, the key must be a uint64_t that has been generated by incrementing
57 * sNextFontFileKey.
58 * Also note that this is _not_ threadsafe.
59 */
Instance()60 static IDWriteFontFileLoader* Instance() {
61 if (!mInstance) {
62 mInstance = new DWriteFontFileLoader();
63 Factory::GetDWriteFactory()->RegisterFontFileLoader(mInstance);
64 }
65 return mInstance;
66 }
67
68 private:
69 static IDWriteFontFileLoader* mInstance;
70 };
71
72 class DWriteFontFileStream final : public IDWriteFontFileStream {
73 public:
74 explicit DWriteFontFileStream(uint64_t aFontFileKey);
75
76 /**
77 * Used by the FontFileLoader to create a new font stream,
78 * this font stream is created from data in memory. The memory
79 * passed may be released after object creation, it will be
80 * copied internally.
81 *
82 * @param aData Font data
83 */
84 bool Initialize(uint8_t* aData, uint32_t aSize);
85
86 // IUnknown interface
IFACEMETHOD(QueryInterface)87 IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) {
88 if (iid == __uuidof(IDWriteFontFileStream)) {
89 *ppObject = static_cast<IDWriteFontFileStream*>(this);
90 return S_OK;
91 } else if (iid == __uuidof(IUnknown)) {
92 *ppObject = static_cast<IUnknown*>(this);
93 return S_OK;
94 } else {
95 return E_NOINTERFACE;
96 }
97 }
98
IFACEMETHOD_(ULONG,AddRef)99 IFACEMETHOD_(ULONG, AddRef)() { return ++mRefCnt; }
100
IFACEMETHOD_(ULONG,Release)101 IFACEMETHOD_(ULONG, Release)() {
102 uint32_t count = --mRefCnt;
103 if (count == 0) {
104 // Avoid locking unless necessary. Verify the refcount hasn't changed
105 // while locked. Delete within the scope of the lock when zero.
106 StaticMutexAutoLock lock(sFontFileStreamsMutex);
107 if (0 != mRefCnt) {
108 return mRefCnt;
109 }
110 delete this;
111 }
112 return count;
113 }
114
115 // IDWriteFontFileStream methods
116 virtual HRESULT STDMETHODCALLTYPE
117 ReadFileFragment(void const** fragmentStart, UINT64 fileOffset,
118 UINT64 fragmentSize, OUT void** fragmentContext);
119
120 virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
121
122 virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
123
124 virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
125
126 private:
127 nsTArray<uint8_t> mData;
128 Atomic<uint32_t> mRefCnt;
129 uint64_t mFontFileKey;
130
131 ~DWriteFontFileStream();
132 };
133
134 IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;
135
CreateStreamFromKey(const void * fontFileReferenceKey,UINT32 fontFileReferenceKeySize,IDWriteFontFileStream ** fontFileStream)136 HRESULT STDMETHODCALLTYPE DWriteFontFileLoader::CreateStreamFromKey(
137 const void* fontFileReferenceKey, UINT32 fontFileReferenceKeySize,
138 IDWriteFontFileStream** fontFileStream) {
139 if (!fontFileReferenceKey || !fontFileStream) {
140 return E_POINTER;
141 }
142
143 StaticMutexAutoLock lock(sFontFileStreamsMutex);
144 uint64_t fontFileKey = *static_cast<const uint64_t*>(fontFileReferenceKey);
145 auto found = sFontFileStreams.find(fontFileKey);
146 if (found == sFontFileStreams.end()) {
147 *fontFileStream = nullptr;
148 return E_FAIL;
149 }
150
151 found->second->AddRef();
152 *fontFileStream = found->second;
153 return S_OK;
154 }
155
DWriteFontFileStream(uint64_t aFontFileKey)156 DWriteFontFileStream::DWriteFontFileStream(uint64_t aFontFileKey)
157 : mRefCnt(0), mFontFileKey(aFontFileKey) {}
158
~DWriteFontFileStream()159 DWriteFontFileStream::~DWriteFontFileStream() {
160 sFontFileStreams.erase(mFontFileKey);
161 }
162
Initialize(uint8_t * aData,uint32_t aSize)163 bool DWriteFontFileStream::Initialize(uint8_t* aData, uint32_t aSize) {
164 if (!mData.SetLength(aSize, fallible)) {
165 return false;
166 }
167 memcpy(mData.Elements(), aData, aSize);
168 return true;
169 }
170
GetFileSize(UINT64 * fileSize)171 HRESULT STDMETHODCALLTYPE DWriteFontFileStream::GetFileSize(UINT64* fileSize) {
172 *fileSize = mData.Length();
173 return S_OK;
174 }
175
176 HRESULT STDMETHODCALLTYPE
GetLastWriteTime(UINT64 * lastWriteTime)177 DWriteFontFileStream::GetLastWriteTime(UINT64* lastWriteTime) {
178 return E_NOTIMPL;
179 }
180
ReadFileFragment(const void ** fragmentStart,UINT64 fileOffset,UINT64 fragmentSize,void ** fragmentContext)181 HRESULT STDMETHODCALLTYPE DWriteFontFileStream::ReadFileFragment(
182 const void** fragmentStart, UINT64 fileOffset, UINT64 fragmentSize,
183 void** fragmentContext) {
184 // We are required to do bounds checking.
185 if (fileOffset + fragmentSize > mData.Length()) {
186 return E_FAIL;
187 }
188
189 // truncate the 64 bit fileOffset to size_t sized index into mData
190 size_t index = static_cast<size_t>(fileOffset);
191
192 // We should be alive for the duration of this.
193 *fragmentStart = &mData[index];
194 *fragmentContext = nullptr;
195 return S_OK;
196 }
197
198 void STDMETHODCALLTYPE
ReleaseFileFragment(void * fragmentContext)199 DWriteFontFileStream::ReleaseFileFragment(void* fragmentContext) {}
200
201 /* static */
Create(uint8_t * aFontData,uint32_t aDataLength)202 already_AddRefed<NativeFontResourceDWrite> NativeFontResourceDWrite::Create(
203 uint8_t* aFontData, uint32_t aDataLength) {
204 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
205 if (!factory) {
206 gfxWarning() << "Failed to get DWrite Factory.";
207 return nullptr;
208 }
209
210 sFontFileStreamsMutex.Lock();
211 uint64_t fontFileKey = sNextFontFileKey++;
212 RefPtr<DWriteFontFileStream> ffsRef = new DWriteFontFileStream(fontFileKey);
213 if (!ffsRef->Initialize(aFontData, aDataLength)) {
214 sFontFileStreamsMutex.Unlock();
215 gfxWarning() << "Failed to create DWriteFontFileStream.";
216 return nullptr;
217 }
218 sFontFileStreams[fontFileKey] = ffsRef;
219 sFontFileStreamsMutex.Unlock();
220
221 RefPtr<IDWriteFontFile> fontFile;
222 HRESULT hr = factory->CreateCustomFontFileReference(
223 &fontFileKey, sizeof(fontFileKey), DWriteFontFileLoader::Instance(),
224 getter_AddRefs(fontFile));
225 if (FAILED(hr)) {
226 gfxWarning() << "Failed to load font file from data!";
227 return nullptr;
228 }
229
230 BOOL isSupported;
231 DWRITE_FONT_FILE_TYPE fileType;
232 DWRITE_FONT_FACE_TYPE faceType;
233 UINT32 numberOfFaces;
234 hr = fontFile->Analyze(&isSupported, &fileType, &faceType, &numberOfFaces);
235 if (FAILED(hr) || !isSupported) {
236 gfxWarning() << "Font file is not supported.";
237 return nullptr;
238 }
239
240 RefPtr<NativeFontResourceDWrite> fontResource =
241 new NativeFontResourceDWrite(factory, fontFile.forget(), ffsRef.forget(),
242 faceType, numberOfFaces, aDataLength);
243 return fontResource.forget();
244 }
245
CreateUnscaledFont(uint32_t aIndex,const uint8_t * aInstanceData,uint32_t aInstanceDataLength)246 already_AddRefed<UnscaledFont> NativeFontResourceDWrite::CreateUnscaledFont(
247 uint32_t aIndex, const uint8_t* aInstanceData,
248 uint32_t aInstanceDataLength) {
249 if (aIndex >= mNumberOfFaces) {
250 gfxWarning() << "Font face index is too high for font resource.";
251 return nullptr;
252 }
253
254 IDWriteFontFile* fontFile = mFontFile;
255 RefPtr<IDWriteFontFace> fontFace;
256 if (FAILED(mFactory->CreateFontFace(mFaceType, 1, &fontFile, aIndex,
257 DWRITE_FONT_SIMULATIONS_NONE,
258 getter_AddRefs(fontFace)))) {
259 gfxWarning() << "Failed to create font face from font file data.";
260 return nullptr;
261 }
262
263 RefPtr<UnscaledFont> unscaledFont = new UnscaledFontDWrite(fontFace, nullptr);
264
265 return unscaledFont.forget();
266 }
267
268 } // namespace gfx
269 } // namespace mozilla
270