/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "NativeFontResourceDWrite.h" #include "UnscaledFontDWrite.h" #include #include "Logging.h" #include "mozilla/RefPtr.h" #include "mozilla/StaticMutex.h" #include "nsTArray.h" namespace mozilla { namespace gfx { static StaticMutex sFontFileStreamsMutex MOZ_UNANNOTATED; static uint64_t sNextFontFileKey = 0; static std::unordered_map sFontFileStreams; class DWriteFontFileLoader : public IDWriteFontFileLoader { public: DWriteFontFileLoader() {} // IUnknown interface IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { if (iid == __uuidof(IDWriteFontFileLoader)) { *ppObject = static_cast(this); return S_OK; } else if (iid == __uuidof(IUnknown)) { *ppObject = static_cast(this); return S_OK; } else { return E_NOINTERFACE; } } IFACEMETHOD_(ULONG, AddRef)() { return 1; } IFACEMETHOD_(ULONG, Release)() { return 1; } // IDWriteFontFileLoader methods /** * Important! Note the key here has to be a uint64_t that will have been * generated by incrementing sNextFontFileKey. */ virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey( void const* fontFileReferenceKey, UINT32 fontFileReferenceKeySize, OUT IDWriteFontFileStream** fontFileStream); /** * Gets the singleton loader instance. Note that when using this font * loader, the key must be a uint64_t that has been generated by incrementing * sNextFontFileKey. * Also note that this is _not_ threadsafe. */ static IDWriteFontFileLoader* Instance() { if (!mInstance) { mInstance = new DWriteFontFileLoader(); Factory::GetDWriteFactory()->RegisterFontFileLoader(mInstance); } return mInstance; } private: static IDWriteFontFileLoader* mInstance; }; class DWriteFontFileStream final : public IDWriteFontFileStream { public: explicit DWriteFontFileStream(uint64_t aFontFileKey); /** * Used by the FontFileLoader to create a new font stream, * this font stream is created from data in memory. The memory * passed may be released after object creation, it will be * copied internally. * * @param aData Font data */ bool Initialize(uint8_t* aData, uint32_t aSize); // IUnknown interface IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) { if (iid == __uuidof(IDWriteFontFileStream)) { *ppObject = static_cast(this); return S_OK; } else if (iid == __uuidof(IUnknown)) { *ppObject = static_cast(this); return S_OK; } else { return E_NOINTERFACE; } } IFACEMETHOD_(ULONG, AddRef)() { return ++mRefCnt; } IFACEMETHOD_(ULONG, Release)() { uint32_t count = --mRefCnt; if (count == 0) { // Avoid locking unless necessary. Verify the refcount hasn't changed // while locked. Delete within the scope of the lock when zero. StaticMutexAutoLock lock(sFontFileStreamsMutex); if (0 != mRefCnt) { return mRefCnt; } delete this; } return count; } // IDWriteFontFileStream methods virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart, UINT64 fileOffset, UINT64 fragmentSize, OUT void** fragmentContext); virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext); virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize); virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime); private: nsTArray mData; Atomic mRefCnt; uint64_t mFontFileKey; ~DWriteFontFileStream(); }; IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr; HRESULT STDMETHODCALLTYPE DWriteFontFileLoader::CreateStreamFromKey( const void* fontFileReferenceKey, UINT32 fontFileReferenceKeySize, IDWriteFontFileStream** fontFileStream) { if (!fontFileReferenceKey || !fontFileStream) { return E_POINTER; } StaticMutexAutoLock lock(sFontFileStreamsMutex); uint64_t fontFileKey = *static_cast(fontFileReferenceKey); auto found = sFontFileStreams.find(fontFileKey); if (found == sFontFileStreams.end()) { *fontFileStream = nullptr; return E_FAIL; } found->second->AddRef(); *fontFileStream = found->second; return S_OK; } DWriteFontFileStream::DWriteFontFileStream(uint64_t aFontFileKey) : mRefCnt(0), mFontFileKey(aFontFileKey) {} DWriteFontFileStream::~DWriteFontFileStream() { sFontFileStreams.erase(mFontFileKey); } bool DWriteFontFileStream::Initialize(uint8_t* aData, uint32_t aSize) { if (!mData.SetLength(aSize, fallible)) { return false; } memcpy(mData.Elements(), aData, aSize); return true; } HRESULT STDMETHODCALLTYPE DWriteFontFileStream::GetFileSize(UINT64* fileSize) { *fileSize = mData.Length(); return S_OK; } HRESULT STDMETHODCALLTYPE DWriteFontFileStream::GetLastWriteTime(UINT64* lastWriteTime) { return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DWriteFontFileStream::ReadFileFragment( const void** fragmentStart, UINT64 fileOffset, UINT64 fragmentSize, void** fragmentContext) { // We are required to do bounds checking. if (fileOffset + fragmentSize > mData.Length()) { return E_FAIL; } // truncate the 64 bit fileOffset to size_t sized index into mData size_t index = static_cast(fileOffset); // We should be alive for the duration of this. *fragmentStart = &mData[index]; *fragmentContext = nullptr; return S_OK; } void STDMETHODCALLTYPE DWriteFontFileStream::ReleaseFileFragment(void* fragmentContext) {} /* static */ already_AddRefed NativeFontResourceDWrite::Create( uint8_t* aFontData, uint32_t aDataLength) { RefPtr factory = Factory::GetDWriteFactory(); if (!factory) { gfxWarning() << "Failed to get DWrite Factory."; return nullptr; } sFontFileStreamsMutex.Lock(); uint64_t fontFileKey = sNextFontFileKey++; RefPtr ffsRef = new DWriteFontFileStream(fontFileKey); if (!ffsRef->Initialize(aFontData, aDataLength)) { sFontFileStreamsMutex.Unlock(); gfxWarning() << "Failed to create DWriteFontFileStream."; return nullptr; } sFontFileStreams[fontFileKey] = ffsRef; sFontFileStreamsMutex.Unlock(); RefPtr fontFile; HRESULT hr = factory->CreateCustomFontFileReference( &fontFileKey, sizeof(fontFileKey), DWriteFontFileLoader::Instance(), getter_AddRefs(fontFile)); if (FAILED(hr)) { gfxWarning() << "Failed to load font file from data!"; return nullptr; } BOOL isSupported; DWRITE_FONT_FILE_TYPE fileType; DWRITE_FONT_FACE_TYPE faceType; UINT32 numberOfFaces; hr = fontFile->Analyze(&isSupported, &fileType, &faceType, &numberOfFaces); if (FAILED(hr) || !isSupported) { gfxWarning() << "Font file is not supported."; return nullptr; } RefPtr fontResource = new NativeFontResourceDWrite(factory, fontFile.forget(), ffsRef.forget(), faceType, numberOfFaces, aDataLength); return fontResource.forget(); } already_AddRefed NativeFontResourceDWrite::CreateUnscaledFont( uint32_t aIndex, const uint8_t* aInstanceData, uint32_t aInstanceDataLength) { if (aIndex >= mNumberOfFaces) { gfxWarning() << "Font face index is too high for font resource."; return nullptr; } IDWriteFontFile* fontFile = mFontFile; RefPtr fontFace; if (FAILED(mFactory->CreateFontFace(mFaceType, 1, &fontFile, aIndex, DWRITE_FONT_SIMULATIONS_NONE, getter_AddRefs(fontFace)))) { gfxWarning() << "Failed to create font face from font file data."; return nullptr; } RefPtr unscaledFont = new UnscaledFontDWrite(fontFace, nullptr); return unscaledFont.forget(); } } // namespace gfx } // namespace mozilla