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