/* -*- Mode: C++; tab-width: 20; 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 "ScaledFontDWrite.h" #include "PathD2D.h" #include "DrawTargetD2D.h" #include "Logging.h" #include namespace mozilla { namespace gfx { struct ffReferenceKey { uint8_t *mData; uint32_t mSize; }; 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 pointer to an * ffReferenceKey object. */ 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 pointer to an FallibleTArray. This * array will be empty when the function returns. */ static IDWriteFontFileLoader* Instance() { if (!mInstance) { mInstance = new DWriteFontFileLoader(); DrawTargetD2D::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); ~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; }; static BYTE GetSystemTextQuality() { BOOL font_smoothing; UINT smoothing_type; if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { return DEFAULT_QUALITY; } if (font_smoothing) { if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing_type, 0)) { return DEFAULT_QUALITY; } if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) { return CLEARTYPE_QUALITY; } return ANTIALIASED_QUALITY; } return DEFAULT_QUALITY; } #define GASP_TAG 0x70736167 #define GASP_DOGRAY 0x2 static inline unsigned short readShort(const char *aBuf) { return (*aBuf << 8) | *(aBuf + 1); } static bool DoGrayscale(IDWriteFontFace *aDWFace, Float ppem) { void *tableContext; char *tableData; UINT32 tableSize; BOOL exists; aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); if (exists) { if (tableSize < 4) { aDWFace->ReleaseFontTable(tableContext); return true; } struct gaspRange { unsigned short maxPPEM; // Stored big-endian unsigned short behavior; // Stored big-endian }; unsigned short numRanges = readShort(tableData + 2); if (tableSize < (UINT)4 + numRanges * 4) { aDWFace->ReleaseFontTable(tableContext); return true; } gaspRange *ranges = (gaspRange *)(tableData + 4); for (int i = 0; i < numRanges; i++) { if (readShort((char*)&ranges[i].maxPPEM) > ppem) { if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) { aDWFace->ReleaseFontTable(tableContext); return false; } break; } } aDWFace->ReleaseFontTable(tableContext); } return true; } IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr; HRESULT STDMETHODCALLTYPE DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, UINT32 fontFileReferenceKeySize, IDWriteFontFileStream **fontFileStream) { if (!fontFileReferenceKey || !fontFileStream) { return E_POINTER; } const ffReferenceKey *key = static_cast(fontFileReferenceKey); *fontFileStream = new DWriteFontFileStream(key->mData, key->mSize); if (!*fontFileStream) { return E_OUTOFMEMORY; } (*fontFileStream)->AddRef(); return S_OK; } DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize) : mRefCnt(0) { mData.resize(aSize); memcpy(&mData.front(), aData, aSize); } DWriteFontFileStream::~DWriteFontFileStream() { } 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) { } ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize, uint32_t aIndex, Float aGlyphSize) : ScaledFontBase(aGlyphSize) { IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); ffReferenceKey key; key.mData = aData; key.mSize = aSize; RefPtr fontFile; if (FAILED(factory->CreateCustomFontFileReference(&key, sizeof(ffReferenceKey), DWriteFontFileLoader::Instance(), getter_AddRefs(fontFile)))) { gfxWarning() << "Failed to load font file from data!"; return; } IDWriteFontFile *ff = fontFile; if (FAILED(factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &ff, aIndex, DWRITE_FONT_SIMULATIONS_NONE, getter_AddRefs(mFontFace)))) { gfxWarning() << "Failed to create font face from font file data!"; } } already_AddRefed ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) { if (aTarget->GetBackendType() != BackendType::DIRECT2D && aTarget->GetBackendType() != BackendType::DIRECT2D1_1) { return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); } RefPtr pathBuilder = aTarget->CreatePathBuilder(); PathBuilderD2D *pathBuilderD2D = static_cast(pathBuilder.get()); CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); return pathBuilder->Finish(); } void ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) { if (aBackendType != BackendType::DIRECT2D && aBackendType != BackendType::DIRECT2D1_1) { ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); return; } PathBuilderD2D *pathBuilderD2D = static_cast(aBuilder); CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); } void ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink) { std::vector indices; std::vector advances; std::vector offsets; indices.resize(aBuffer.mNumGlyphs); advances.resize(aBuffer.mNumGlyphs); offsets.resize(aBuffer.mNumGlyphs); memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs); for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { indices[i] = aBuffer.mGlyphs[i].mIndex; offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x; offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y; } mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(), &offsets.front(), aBuffer.mNumGlyphs, FALSE, FALSE, aSink); } bool ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) { UINT32 fileCount = 0; mFontFace->GetFiles(&fileCount, nullptr); if (fileCount > 1) { MOZ_ASSERT(false); return false; } RefPtr file; mFontFace->GetFiles(&fileCount, getter_AddRefs(file)); const void *referenceKey; UINT32 refKeySize; // XXX - This can currently crash for webfonts, as when we get the reference // key out of the file, that can be an invalid reference key for the loader // we use it with. The fix to this is not obvious but it will probably // have to happen inside thebes. file->GetReferenceKey(&referenceKey, &refKeySize); RefPtr loader; file->GetLoader(getter_AddRefs(loader)); RefPtr stream; loader->CreateStreamFromKey(referenceKey, refKeySize, getter_AddRefs(stream)); UINT64 fileSize64; stream->GetFileSize(&fileSize64); if (fileSize64 > UINT32_MAX) { MOZ_ASSERT(false); return false; } uint32_t fileSize = static_cast(fileSize64); const void *fragmentStart; void *context; stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context); aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton); stream->ReleaseFileFragment(context); return true; } AntialiasMode ScaledFontDWrite::GetDefaultAAMode() { AntialiasMode defaultMode = AntialiasMode::SUBPIXEL; switch (GetSystemTextQuality()) { case CLEARTYPE_QUALITY: defaultMode = AntialiasMode::SUBPIXEL; break; case ANTIALIASED_QUALITY: defaultMode = AntialiasMode::GRAY; break; case DEFAULT_QUALITY: defaultMode = AntialiasMode::NONE; break; } if (defaultMode == AntialiasMode::GRAY) { if (!DoGrayscale(mFontFace, mSize)) { defaultMode = AntialiasMode::NONE; } } return defaultMode; } } }