/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 #include "DrawTargetD2D1.h" #include "Logging.h" #include "mozilla/RefPtr.h" namespace mozilla { namespace gfx { static Atomic sNextFontFileKey; 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(); DrawTargetD2D1::GetDWriteFactory()-> RegisterFontFileLoader(mInstance); } return mInstance; } private: static IDWriteFontFileLoader* mInstance; }; class DWriteFontFileStream : public IDWriteFontFileStream { public: /** * 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 */ DWriteFontFileStream(uint8_t *aData, uint32_t aSize, uint64_t aFontFileKey); ~DWriteFontFileStream(); // 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)() { ++mRefCnt; return mRefCnt; } IFACEMETHOD_(ULONG, Release)() { --mRefCnt; if (mRefCnt == 0) { delete this; return 0; } return mRefCnt; } // 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: std::vector mData; uint32_t mRefCnt; uint64_t mFontFileKey; }; IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr; HRESULT STDMETHODCALLTYPE DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, UINT32 fontFileReferenceKeySize, IDWriteFontFileStream **fontFileStream) { if (!fontFileReferenceKey || !fontFileStream) { return E_POINTER; } 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(uint8_t *aData, uint32_t aSize, uint64_t aFontFileKey) : mRefCnt(0) , mFontFileKey(aFontFileKey) { mData.resize(aSize); memcpy(&mData.front(), aData, aSize); } DWriteFontFileStream::~DWriteFontFileStream() { sFontFileStreams.erase(mFontFileKey); } HRESULT STDMETHODCALLTYPE DWriteFontFileStream::GetFileSize(UINT64 *fileSize) { *fileSize = mData.size(); 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.size()) { 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, bool aNeedsCairo) { IDWriteFactory *factory = DrawTargetD2D1::GetDWriteFactory(); if (!factory) { gfxWarning() << "Failed to get DWrite Factory."; return nullptr; } uint64_t fontFileKey = sNextFontFileKey++; RefPtr ffsRef = new DWriteFontFileStream(aFontData, aDataLength, fontFileKey); sFontFileStreams[fontFileKey] = ffsRef; 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(), faceType, numberOfFaces, aNeedsCairo); return fontResource.forget(); } already_AddRefed NativeFontResourceDWrite::CreateScaledFont(uint32_t aIndex, Float aGlyphSize, 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 scaledFont = new ScaledFontDWrite(fontFace, aGlyphSize); if (mNeedsCairo && !scaledFont->PopulateCairoScaledFont()) { gfxWarning() << "Unable to create cairo scaled font DWrite font."; return nullptr; } return scaledFont.forget(); } } // gfx } // mozilla