michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ScaledFontDWrite.h" michael@0: #include "PathD2D.h" michael@0: #include "DrawTargetD2D.h" michael@0: #include "Logging.h" michael@0: michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: struct ffReferenceKey michael@0: { michael@0: uint8_t *mData; michael@0: uint32_t mSize; michael@0: }; michael@0: michael@0: class DWriteFontFileLoader : public IDWriteFontFileLoader michael@0: { michael@0: public: michael@0: DWriteFontFileLoader() michael@0: { michael@0: } michael@0: michael@0: // IUnknown interface michael@0: IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) michael@0: { michael@0: if (iid == __uuidof(IDWriteFontFileLoader)) { michael@0: *ppObject = static_cast(this); michael@0: return S_OK; michael@0: } else if (iid == __uuidof(IUnknown)) { michael@0: *ppObject = static_cast(this); michael@0: return S_OK; michael@0: } else { michael@0: return E_NOINTERFACE; michael@0: } michael@0: } michael@0: michael@0: IFACEMETHOD_(ULONG, AddRef)() michael@0: { michael@0: return 1; michael@0: } michael@0: michael@0: IFACEMETHOD_(ULONG, Release)() michael@0: { michael@0: return 1; michael@0: } michael@0: michael@0: // IDWriteFontFileLoader methods michael@0: /** michael@0: * Important! Note the key here -has- to be a pointer to an michael@0: * ffReferenceKey object. michael@0: */ michael@0: virtual HRESULT STDMETHODCALLTYPE michael@0: CreateStreamFromKey(void const* fontFileReferenceKey, michael@0: UINT32 fontFileReferenceKeySize, michael@0: OUT IDWriteFontFileStream** fontFileStream); michael@0: michael@0: /** michael@0: * Gets the singleton loader instance. Note that when using this font michael@0: * loader, the key must be a pointer to an FallibleTArray. This michael@0: * array will be empty when the function returns. michael@0: */ michael@0: static IDWriteFontFileLoader* Instance() michael@0: { michael@0: if (!mInstance) { michael@0: mInstance = new DWriteFontFileLoader(); michael@0: DrawTargetD2D::GetDWriteFactory()-> michael@0: RegisterFontFileLoader(mInstance); michael@0: } michael@0: return mInstance; michael@0: } michael@0: michael@0: private: michael@0: static IDWriteFontFileLoader* mInstance; michael@0: }; michael@0: michael@0: class DWriteFontFileStream : public IDWriteFontFileStream michael@0: { michael@0: public: michael@0: /** michael@0: * Used by the FontFileLoader to create a new font stream, michael@0: * this font stream is created from data in memory. The memory michael@0: * passed may be released after object creation, it will be michael@0: * copied internally. michael@0: * michael@0: * @param aData Font data michael@0: */ michael@0: DWriteFontFileStream(uint8_t *aData, uint32_t aSize); michael@0: ~DWriteFontFileStream(); michael@0: michael@0: // IUnknown interface michael@0: IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) michael@0: { michael@0: if (iid == __uuidof(IDWriteFontFileStream)) { michael@0: *ppObject = static_cast(this); michael@0: return S_OK; michael@0: } else if (iid == __uuidof(IUnknown)) { michael@0: *ppObject = static_cast(this); michael@0: return S_OK; michael@0: } else { michael@0: return E_NOINTERFACE; michael@0: } michael@0: } michael@0: michael@0: IFACEMETHOD_(ULONG, AddRef)() michael@0: { michael@0: ++mRefCnt; michael@0: return mRefCnt; michael@0: } michael@0: michael@0: IFACEMETHOD_(ULONG, Release)() michael@0: { michael@0: --mRefCnt; michael@0: if (mRefCnt == 0) { michael@0: delete this; michael@0: return 0; michael@0: } michael@0: return mRefCnt; michael@0: } michael@0: michael@0: // IDWriteFontFileStream methods michael@0: virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart, michael@0: UINT64 fileOffset, michael@0: UINT64 fragmentSize, michael@0: OUT void** fragmentContext); michael@0: michael@0: virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext); michael@0: michael@0: virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize); michael@0: michael@0: virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime); michael@0: michael@0: private: michael@0: std::vector mData; michael@0: uint32_t mRefCnt; michael@0: }; michael@0: michael@0: static BYTE michael@0: GetSystemTextQuality() michael@0: { michael@0: BOOL font_smoothing; michael@0: UINT smoothing_type; michael@0: michael@0: if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { michael@0: return DEFAULT_QUALITY; michael@0: } michael@0: michael@0: if (font_smoothing) { michael@0: if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, michael@0: 0, &smoothing_type, 0)) { michael@0: return DEFAULT_QUALITY; michael@0: } michael@0: michael@0: if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) { michael@0: return CLEARTYPE_QUALITY; michael@0: } michael@0: michael@0: return ANTIALIASED_QUALITY; michael@0: } michael@0: michael@0: return DEFAULT_QUALITY; michael@0: } michael@0: michael@0: #define GASP_TAG 0x70736167 michael@0: #define GASP_DOGRAY 0x2 michael@0: michael@0: static inline unsigned short michael@0: readShort(const char *aBuf) michael@0: { michael@0: return (*aBuf << 8) | *(aBuf + 1); michael@0: } michael@0: michael@0: static bool michael@0: DoGrayscale(IDWriteFontFace *aDWFace, Float ppem) michael@0: { michael@0: void *tableContext; michael@0: char *tableData; michael@0: UINT32 tableSize; michael@0: BOOL exists; michael@0: aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); michael@0: michael@0: if (exists) { michael@0: if (tableSize < 4) { michael@0: aDWFace->ReleaseFontTable(tableContext); michael@0: return true; michael@0: } michael@0: struct gaspRange { michael@0: unsigned short maxPPEM; // Stored big-endian michael@0: unsigned short behavior; // Stored big-endian michael@0: }; michael@0: unsigned short numRanges = readShort(tableData + 2); michael@0: if (tableSize < (UINT)4 + numRanges * 4) { michael@0: aDWFace->ReleaseFontTable(tableContext); michael@0: return true; michael@0: } michael@0: gaspRange *ranges = (gaspRange *)(tableData + 4); michael@0: for (int i = 0; i < numRanges; i++) { michael@0: if (readShort((char*)&ranges[i].maxPPEM) > ppem) { michael@0: if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) { michael@0: aDWFace->ReleaseFontTable(tableContext); michael@0: return false; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: aDWFace->ReleaseFontTable(tableContext); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr; michael@0: michael@0: HRESULT STDMETHODCALLTYPE michael@0: DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, michael@0: UINT32 fontFileReferenceKeySize, michael@0: IDWriteFontFileStream **fontFileStream) michael@0: { michael@0: if (!fontFileReferenceKey || !fontFileStream) { michael@0: return E_POINTER; michael@0: } michael@0: michael@0: const ffReferenceKey *key = static_cast(fontFileReferenceKey); michael@0: *fontFileStream = michael@0: new DWriteFontFileStream(key->mData, key->mSize); michael@0: michael@0: if (!*fontFileStream) { michael@0: return E_OUTOFMEMORY; michael@0: } michael@0: (*fontFileStream)->AddRef(); michael@0: return S_OK; michael@0: } michael@0: michael@0: DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize) michael@0: : mRefCnt(0) michael@0: { michael@0: mData.resize(aSize); michael@0: memcpy(&mData.front(), aData, aSize); michael@0: } michael@0: michael@0: DWriteFontFileStream::~DWriteFontFileStream() michael@0: { michael@0: } michael@0: michael@0: HRESULT STDMETHODCALLTYPE michael@0: DWriteFontFileStream::GetFileSize(UINT64 *fileSize) michael@0: { michael@0: *fileSize = mData.size(); michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT STDMETHODCALLTYPE michael@0: DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) michael@0: { michael@0: return E_NOTIMPL; michael@0: } michael@0: michael@0: HRESULT STDMETHODCALLTYPE michael@0: DWriteFontFileStream::ReadFileFragment(const void **fragmentStart, michael@0: UINT64 fileOffset, michael@0: UINT64 fragmentSize, michael@0: void **fragmentContext) michael@0: { michael@0: // We are required to do bounds checking. michael@0: if (fileOffset + fragmentSize > mData.size()) { michael@0: return E_FAIL; michael@0: } michael@0: michael@0: // truncate the 64 bit fileOffset to size_t sized index into mData michael@0: size_t index = static_cast(fileOffset); michael@0: michael@0: // We should be alive for the duration of this. michael@0: *fragmentStart = &mData[index]; michael@0: *fragmentContext = nullptr; michael@0: return S_OK; michael@0: } michael@0: michael@0: void STDMETHODCALLTYPE michael@0: DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext) michael@0: { michael@0: } michael@0: michael@0: ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize, michael@0: uint32_t aIndex, Float aGlyphSize) michael@0: : ScaledFontBase(aGlyphSize) michael@0: { michael@0: IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); michael@0: michael@0: ffReferenceKey key; michael@0: key.mData = aData; michael@0: key.mSize = aSize; michael@0: michael@0: RefPtr fontFile; michael@0: if (FAILED(factory->CreateCustomFontFileReference(&key, sizeof(ffReferenceKey), DWriteFontFileLoader::Instance(), byRef(fontFile)))) { michael@0: gfxWarning() << "Failed to load font file from data!"; michael@0: return; michael@0: } michael@0: michael@0: IDWriteFontFile *ff = fontFile; michael@0: if (FAILED(factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &ff, aIndex, DWRITE_FONT_SIMULATIONS_NONE, byRef(mFontFace)))) { michael@0: gfxWarning() << "Failed to create font face from font file data!"; michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) michael@0: { michael@0: if (aTarget->GetType() != BackendType::DIRECT2D) { michael@0: return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); michael@0: } michael@0: michael@0: RefPtr pathBuilder = aTarget->CreatePathBuilder(); michael@0: michael@0: PathBuilderD2D *pathBuilderD2D = michael@0: static_cast(pathBuilder.get()); michael@0: michael@0: CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); michael@0: michael@0: return pathBuilder->Finish(); michael@0: } michael@0: michael@0: void michael@0: ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) michael@0: { michael@0: if (aBackendType != BackendType::DIRECT2D) { michael@0: ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); michael@0: return; michael@0: } michael@0: michael@0: PathBuilderD2D *pathBuilderD2D = michael@0: static_cast(aBuilder); michael@0: michael@0: CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); michael@0: } michael@0: michael@0: void michael@0: ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink) michael@0: { michael@0: std::vector indices; michael@0: std::vector advances; michael@0: std::vector offsets; michael@0: indices.resize(aBuffer.mNumGlyphs); michael@0: advances.resize(aBuffer.mNumGlyphs); michael@0: offsets.resize(aBuffer.mNumGlyphs); michael@0: michael@0: memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs); michael@0: for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { michael@0: indices[i] = aBuffer.mGlyphs[i].mIndex; michael@0: offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x; michael@0: offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y; michael@0: } michael@0: michael@0: mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(), michael@0: &offsets.front(), aBuffer.mNumGlyphs, michael@0: FALSE, FALSE, aSink); michael@0: } michael@0: michael@0: bool michael@0: ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) michael@0: { michael@0: UINT32 fileCount = 0; michael@0: mFontFace->GetFiles(&fileCount, nullptr); michael@0: michael@0: if (fileCount > 1) { michael@0: MOZ_ASSERT(false); michael@0: return false; michael@0: } michael@0: michael@0: RefPtr file; michael@0: mFontFace->GetFiles(&fileCount, byRef(file)); michael@0: michael@0: const void *referenceKey; michael@0: UINT32 refKeySize; michael@0: // XXX - This can currently crash for webfonts, as when we get the reference michael@0: // key out of the file, that can be an invalid reference key for the loader michael@0: // we use it with. The fix to this is not obvious but it will probably michael@0: // have to happen inside thebes. michael@0: file->GetReferenceKey(&referenceKey, &refKeySize); michael@0: michael@0: RefPtr loader; michael@0: file->GetLoader(byRef(loader)); michael@0: michael@0: RefPtr stream; michael@0: loader->CreateStreamFromKey(referenceKey, refKeySize, byRef(stream)); michael@0: michael@0: UINT64 fileSize64; michael@0: stream->GetFileSize(&fileSize64); michael@0: if (fileSize64 > UINT32_MAX) { michael@0: MOZ_ASSERT(false); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t fileSize = static_cast(fileSize64); michael@0: const void *fragmentStart; michael@0: void *context; michael@0: stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context); michael@0: michael@0: aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton); michael@0: michael@0: stream->ReleaseFileFragment(context); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: AntialiasMode michael@0: ScaledFontDWrite::GetDefaultAAMode() michael@0: { michael@0: AntialiasMode defaultMode = AntialiasMode::SUBPIXEL; michael@0: michael@0: switch (GetSystemTextQuality()) { michael@0: case CLEARTYPE_QUALITY: michael@0: defaultMode = AntialiasMode::SUBPIXEL; michael@0: break; michael@0: case ANTIALIASED_QUALITY: michael@0: defaultMode = AntialiasMode::GRAY; michael@0: break; michael@0: case DEFAULT_QUALITY: michael@0: defaultMode = AntialiasMode::NONE; michael@0: break; michael@0: } michael@0: michael@0: if (defaultMode == AntialiasMode::GRAY) { michael@0: if (!DoGrayscale(mFontFace, mSize)) { michael@0: defaultMode = AntialiasMode::NONE; michael@0: } michael@0: } michael@0: return defaultMode; michael@0: } michael@0: michael@0: } michael@0: }