diff -r 000000000000 -r 6474c204b198 gfx/2d/ScaledFontDWrite.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/2d/ScaledFontDWrite.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,435 @@ +/* -*- 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(), byRef(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, byRef(mFontFace)))) { + gfxWarning() << "Failed to create font face from font file data!"; + } +} + +TemporaryRef +ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) +{ + if (aTarget->GetType() != BackendType::DIRECT2D) { + 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) { + 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, byRef(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(byRef(loader)); + + RefPtr stream; + loader->CreateStreamFromKey(referenceKey, refKeySize, byRef(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; +} + +} +}