gecko-dev/gfx/2d/NativeFontResourceDWrite.cpp
2020-05-25 07:42:38 +00:00

270 lines
8.2 KiB
C++

/* -*- 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 <unordered_map>
#include "Logging.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticMutex.h"
#include "nsTArray.h"
namespace mozilla {
namespace gfx {
static StaticMutex sFontFileStreamsMutex;
static uint64_t sNextFontFileKey = 0;
static std::unordered_map<uint64_t, IDWriteFontFileStream*> sFontFileStreams;
class DWriteFontFileLoader : public IDWriteFontFileLoader {
public:
DWriteFontFileLoader() {}
// IUnknown interface
IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) {
if (iid == __uuidof(IDWriteFontFileLoader)) {
*ppObject = static_cast<IDWriteFontFileLoader*>(this);
return S_OK;
} else if (iid == __uuidof(IUnknown)) {
*ppObject = static_cast<IUnknown*>(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<IDWriteFontFileStream*>(this);
return S_OK;
} else if (iid == __uuidof(IUnknown)) {
*ppObject = static_cast<IUnknown*>(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<uint8_t> mData;
Atomic<uint32_t> 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<const uint64_t*>(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<size_t>(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> NativeFontResourceDWrite::Create(
uint8_t* aFontData, uint32_t aDataLength) {
RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
if (!factory) {
gfxWarning() << "Failed to get DWrite Factory.";
return nullptr;
}
sFontFileStreamsMutex.Lock();
uint64_t fontFileKey = sNextFontFileKey++;
RefPtr<DWriteFontFileStream> ffsRef = new DWriteFontFileStream(fontFileKey);
if (!ffsRef->Initialize(aFontData, aDataLength)) {
sFontFileStreamsMutex.Unlock();
gfxWarning() << "Failed to create DWriteFontFileStream.";
return nullptr;
}
sFontFileStreams[fontFileKey] = ffsRef;
sFontFileStreamsMutex.Unlock();
RefPtr<IDWriteFontFile> 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<NativeFontResourceDWrite> fontResource =
new NativeFontResourceDWrite(factory, fontFile.forget(), ffsRef.forget(),
faceType, numberOfFaces, aDataLength);
return fontResource.forget();
}
already_AddRefed<UnscaledFont> 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<IDWriteFontFace> 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> unscaledFont = new UnscaledFontDWrite(fontFace, nullptr);
return unscaledFont.forget();
}
} // namespace gfx
} // namespace mozilla