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