1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/ScaledFontDWrite.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,435 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "ScaledFontDWrite.h" 1.10 +#include "PathD2D.h" 1.11 +#include "DrawTargetD2D.h" 1.12 +#include "Logging.h" 1.13 + 1.14 +#include <vector> 1.15 + 1.16 +namespace mozilla { 1.17 +namespace gfx { 1.18 + 1.19 +struct ffReferenceKey 1.20 +{ 1.21 + uint8_t *mData; 1.22 + uint32_t mSize; 1.23 +}; 1.24 + 1.25 +class DWriteFontFileLoader : public IDWriteFontFileLoader 1.26 +{ 1.27 +public: 1.28 + DWriteFontFileLoader() 1.29 + { 1.30 + } 1.31 + 1.32 + // IUnknown interface 1.33 + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) 1.34 + { 1.35 + if (iid == __uuidof(IDWriteFontFileLoader)) { 1.36 + *ppObject = static_cast<IDWriteFontFileLoader*>(this); 1.37 + return S_OK; 1.38 + } else if (iid == __uuidof(IUnknown)) { 1.39 + *ppObject = static_cast<IUnknown*>(this); 1.40 + return S_OK; 1.41 + } else { 1.42 + return E_NOINTERFACE; 1.43 + } 1.44 + } 1.45 + 1.46 + IFACEMETHOD_(ULONG, AddRef)() 1.47 + { 1.48 + return 1; 1.49 + } 1.50 + 1.51 + IFACEMETHOD_(ULONG, Release)() 1.52 + { 1.53 + return 1; 1.54 + } 1.55 + 1.56 + // IDWriteFontFileLoader methods 1.57 + /** 1.58 + * Important! Note the key here -has- to be a pointer to an 1.59 + * ffReferenceKey object. 1.60 + */ 1.61 + virtual HRESULT STDMETHODCALLTYPE 1.62 + CreateStreamFromKey(void const* fontFileReferenceKey, 1.63 + UINT32 fontFileReferenceKeySize, 1.64 + OUT IDWriteFontFileStream** fontFileStream); 1.65 + 1.66 + /** 1.67 + * Gets the singleton loader instance. Note that when using this font 1.68 + * loader, the key must be a pointer to an FallibleTArray<uint8_t>. This 1.69 + * array will be empty when the function returns. 1.70 + */ 1.71 + static IDWriteFontFileLoader* Instance() 1.72 + { 1.73 + if (!mInstance) { 1.74 + mInstance = new DWriteFontFileLoader(); 1.75 + DrawTargetD2D::GetDWriteFactory()-> 1.76 + RegisterFontFileLoader(mInstance); 1.77 + } 1.78 + return mInstance; 1.79 + } 1.80 + 1.81 +private: 1.82 + static IDWriteFontFileLoader* mInstance; 1.83 +}; 1.84 + 1.85 +class DWriteFontFileStream : public IDWriteFontFileStream 1.86 +{ 1.87 +public: 1.88 + /** 1.89 + * Used by the FontFileLoader to create a new font stream, 1.90 + * this font stream is created from data in memory. The memory 1.91 + * passed may be released after object creation, it will be 1.92 + * copied internally. 1.93 + * 1.94 + * @param aData Font data 1.95 + */ 1.96 + DWriteFontFileStream(uint8_t *aData, uint32_t aSize); 1.97 + ~DWriteFontFileStream(); 1.98 + 1.99 + // IUnknown interface 1.100 + IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) 1.101 + { 1.102 + if (iid == __uuidof(IDWriteFontFileStream)) { 1.103 + *ppObject = static_cast<IDWriteFontFileStream*>(this); 1.104 + return S_OK; 1.105 + } else if (iid == __uuidof(IUnknown)) { 1.106 + *ppObject = static_cast<IUnknown*>(this); 1.107 + return S_OK; 1.108 + } else { 1.109 + return E_NOINTERFACE; 1.110 + } 1.111 + } 1.112 + 1.113 + IFACEMETHOD_(ULONG, AddRef)() 1.114 + { 1.115 + ++mRefCnt; 1.116 + return mRefCnt; 1.117 + } 1.118 + 1.119 + IFACEMETHOD_(ULONG, Release)() 1.120 + { 1.121 + --mRefCnt; 1.122 + if (mRefCnt == 0) { 1.123 + delete this; 1.124 + return 0; 1.125 + } 1.126 + return mRefCnt; 1.127 + } 1.128 + 1.129 + // IDWriteFontFileStream methods 1.130 + virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart, 1.131 + UINT64 fileOffset, 1.132 + UINT64 fragmentSize, 1.133 + OUT void** fragmentContext); 1.134 + 1.135 + virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext); 1.136 + 1.137 + virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize); 1.138 + 1.139 + virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime); 1.140 + 1.141 +private: 1.142 + std::vector<uint8_t> mData; 1.143 + uint32_t mRefCnt; 1.144 +}; 1.145 + 1.146 +static BYTE 1.147 +GetSystemTextQuality() 1.148 +{ 1.149 + BOOL font_smoothing; 1.150 + UINT smoothing_type; 1.151 + 1.152 + if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { 1.153 + return DEFAULT_QUALITY; 1.154 + } 1.155 + 1.156 + if (font_smoothing) { 1.157 + if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 1.158 + 0, &smoothing_type, 0)) { 1.159 + return DEFAULT_QUALITY; 1.160 + } 1.161 + 1.162 + if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) { 1.163 + return CLEARTYPE_QUALITY; 1.164 + } 1.165 + 1.166 + return ANTIALIASED_QUALITY; 1.167 + } 1.168 + 1.169 + return DEFAULT_QUALITY; 1.170 +} 1.171 + 1.172 +#define GASP_TAG 0x70736167 1.173 +#define GASP_DOGRAY 0x2 1.174 + 1.175 +static inline unsigned short 1.176 +readShort(const char *aBuf) 1.177 +{ 1.178 + return (*aBuf << 8) | *(aBuf + 1); 1.179 +} 1.180 + 1.181 +static bool 1.182 +DoGrayscale(IDWriteFontFace *aDWFace, Float ppem) 1.183 +{ 1.184 + void *tableContext; 1.185 + char *tableData; 1.186 + UINT32 tableSize; 1.187 + BOOL exists; 1.188 + aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); 1.189 + 1.190 + if (exists) { 1.191 + if (tableSize < 4) { 1.192 + aDWFace->ReleaseFontTable(tableContext); 1.193 + return true; 1.194 + } 1.195 + struct gaspRange { 1.196 + unsigned short maxPPEM; // Stored big-endian 1.197 + unsigned short behavior; // Stored big-endian 1.198 + }; 1.199 + unsigned short numRanges = readShort(tableData + 2); 1.200 + if (tableSize < (UINT)4 + numRanges * 4) { 1.201 + aDWFace->ReleaseFontTable(tableContext); 1.202 + return true; 1.203 + } 1.204 + gaspRange *ranges = (gaspRange *)(tableData + 4); 1.205 + for (int i = 0; i < numRanges; i++) { 1.206 + if (readShort((char*)&ranges[i].maxPPEM) > ppem) { 1.207 + if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) { 1.208 + aDWFace->ReleaseFontTable(tableContext); 1.209 + return false; 1.210 + } 1.211 + break; 1.212 + } 1.213 + } 1.214 + aDWFace->ReleaseFontTable(tableContext); 1.215 + } 1.216 + return true; 1.217 +} 1.218 + 1.219 +IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr; 1.220 + 1.221 +HRESULT STDMETHODCALLTYPE 1.222 +DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, 1.223 + UINT32 fontFileReferenceKeySize, 1.224 + IDWriteFontFileStream **fontFileStream) 1.225 +{ 1.226 + if (!fontFileReferenceKey || !fontFileStream) { 1.227 + return E_POINTER; 1.228 + } 1.229 + 1.230 + const ffReferenceKey *key = static_cast<const ffReferenceKey*>(fontFileReferenceKey); 1.231 + *fontFileStream = 1.232 + new DWriteFontFileStream(key->mData, key->mSize); 1.233 + 1.234 + if (!*fontFileStream) { 1.235 + return E_OUTOFMEMORY; 1.236 + } 1.237 + (*fontFileStream)->AddRef(); 1.238 + return S_OK; 1.239 +} 1.240 + 1.241 +DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize) 1.242 + : mRefCnt(0) 1.243 +{ 1.244 + mData.resize(aSize); 1.245 + memcpy(&mData.front(), aData, aSize); 1.246 +} 1.247 + 1.248 +DWriteFontFileStream::~DWriteFontFileStream() 1.249 +{ 1.250 +} 1.251 + 1.252 +HRESULT STDMETHODCALLTYPE 1.253 +DWriteFontFileStream::GetFileSize(UINT64 *fileSize) 1.254 +{ 1.255 + *fileSize = mData.size(); 1.256 + return S_OK; 1.257 +} 1.258 + 1.259 +HRESULT STDMETHODCALLTYPE 1.260 +DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) 1.261 +{ 1.262 + return E_NOTIMPL; 1.263 +} 1.264 + 1.265 +HRESULT STDMETHODCALLTYPE 1.266 +DWriteFontFileStream::ReadFileFragment(const void **fragmentStart, 1.267 + UINT64 fileOffset, 1.268 + UINT64 fragmentSize, 1.269 + void **fragmentContext) 1.270 +{ 1.271 + // We are required to do bounds checking. 1.272 + if (fileOffset + fragmentSize > mData.size()) { 1.273 + return E_FAIL; 1.274 + } 1.275 + 1.276 + // truncate the 64 bit fileOffset to size_t sized index into mData 1.277 + size_t index = static_cast<size_t>(fileOffset); 1.278 + 1.279 + // We should be alive for the duration of this. 1.280 + *fragmentStart = &mData[index]; 1.281 + *fragmentContext = nullptr; 1.282 + return S_OK; 1.283 +} 1.284 + 1.285 +void STDMETHODCALLTYPE 1.286 +DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext) 1.287 +{ 1.288 +} 1.289 + 1.290 +ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize, 1.291 + uint32_t aIndex, Float aGlyphSize) 1.292 + : ScaledFontBase(aGlyphSize) 1.293 +{ 1.294 + IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); 1.295 + 1.296 + ffReferenceKey key; 1.297 + key.mData = aData; 1.298 + key.mSize = aSize; 1.299 + 1.300 + RefPtr<IDWriteFontFile> fontFile; 1.301 + if (FAILED(factory->CreateCustomFontFileReference(&key, sizeof(ffReferenceKey), DWriteFontFileLoader::Instance(), byRef(fontFile)))) { 1.302 + gfxWarning() << "Failed to load font file from data!"; 1.303 + return; 1.304 + } 1.305 + 1.306 + IDWriteFontFile *ff = fontFile; 1.307 + if (FAILED(factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &ff, aIndex, DWRITE_FONT_SIMULATIONS_NONE, byRef(mFontFace)))) { 1.308 + gfxWarning() << "Failed to create font face from font file data!"; 1.309 + } 1.310 +} 1.311 + 1.312 +TemporaryRef<Path> 1.313 +ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) 1.314 +{ 1.315 + if (aTarget->GetType() != BackendType::DIRECT2D) { 1.316 + return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); 1.317 + } 1.318 + 1.319 + RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder(); 1.320 + 1.321 + PathBuilderD2D *pathBuilderD2D = 1.322 + static_cast<PathBuilderD2D*>(pathBuilder.get()); 1.323 + 1.324 + CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); 1.325 + 1.326 + return pathBuilder->Finish(); 1.327 +} 1.328 + 1.329 +void 1.330 +ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) 1.331 +{ 1.332 + if (aBackendType != BackendType::DIRECT2D) { 1.333 + ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); 1.334 + return; 1.335 + } 1.336 + 1.337 + PathBuilderD2D *pathBuilderD2D = 1.338 + static_cast<PathBuilderD2D*>(aBuilder); 1.339 + 1.340 + CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); 1.341 +} 1.342 + 1.343 +void 1.344 +ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink) 1.345 +{ 1.346 + std::vector<UINT16> indices; 1.347 + std::vector<FLOAT> advances; 1.348 + std::vector<DWRITE_GLYPH_OFFSET> offsets; 1.349 + indices.resize(aBuffer.mNumGlyphs); 1.350 + advances.resize(aBuffer.mNumGlyphs); 1.351 + offsets.resize(aBuffer.mNumGlyphs); 1.352 + 1.353 + memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs); 1.354 + for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { 1.355 + indices[i] = aBuffer.mGlyphs[i].mIndex; 1.356 + offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x; 1.357 + offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y; 1.358 + } 1.359 + 1.360 + mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(), 1.361 + &offsets.front(), aBuffer.mNumGlyphs, 1.362 + FALSE, FALSE, aSink); 1.363 +} 1.364 + 1.365 +bool 1.366 +ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) 1.367 +{ 1.368 + UINT32 fileCount = 0; 1.369 + mFontFace->GetFiles(&fileCount, nullptr); 1.370 + 1.371 + if (fileCount > 1) { 1.372 + MOZ_ASSERT(false); 1.373 + return false; 1.374 + } 1.375 + 1.376 + RefPtr<IDWriteFontFile> file; 1.377 + mFontFace->GetFiles(&fileCount, byRef(file)); 1.378 + 1.379 + const void *referenceKey; 1.380 + UINT32 refKeySize; 1.381 + // XXX - This can currently crash for webfonts, as when we get the reference 1.382 + // key out of the file, that can be an invalid reference key for the loader 1.383 + // we use it with. The fix to this is not obvious but it will probably 1.384 + // have to happen inside thebes. 1.385 + file->GetReferenceKey(&referenceKey, &refKeySize); 1.386 + 1.387 + RefPtr<IDWriteFontFileLoader> loader; 1.388 + file->GetLoader(byRef(loader)); 1.389 + 1.390 + RefPtr<IDWriteFontFileStream> stream; 1.391 + loader->CreateStreamFromKey(referenceKey, refKeySize, byRef(stream)); 1.392 + 1.393 + UINT64 fileSize64; 1.394 + stream->GetFileSize(&fileSize64); 1.395 + if (fileSize64 > UINT32_MAX) { 1.396 + MOZ_ASSERT(false); 1.397 + return false; 1.398 + } 1.399 + 1.400 + uint32_t fileSize = static_cast<uint32_t>(fileSize64); 1.401 + const void *fragmentStart; 1.402 + void *context; 1.403 + stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context); 1.404 + 1.405 + aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton); 1.406 + 1.407 + stream->ReleaseFileFragment(context); 1.408 + 1.409 + return true; 1.410 +} 1.411 + 1.412 +AntialiasMode 1.413 +ScaledFontDWrite::GetDefaultAAMode() 1.414 +{ 1.415 + AntialiasMode defaultMode = AntialiasMode::SUBPIXEL; 1.416 + 1.417 + switch (GetSystemTextQuality()) { 1.418 + case CLEARTYPE_QUALITY: 1.419 + defaultMode = AntialiasMode::SUBPIXEL; 1.420 + break; 1.421 + case ANTIALIASED_QUALITY: 1.422 + defaultMode = AntialiasMode::GRAY; 1.423 + break; 1.424 + case DEFAULT_QUALITY: 1.425 + defaultMode = AntialiasMode::NONE; 1.426 + break; 1.427 + } 1.428 + 1.429 + if (defaultMode == AntialiasMode::GRAY) { 1.430 + if (!DoGrayscale(mFontFace, mSize)) { 1.431 + defaultMode = AntialiasMode::NONE; 1.432 + } 1.433 + } 1.434 + return defaultMode; 1.435 +} 1.436 + 1.437 +} 1.438 +}