Merge m-c to inbound.
[gecko.git] / gfx / 2d / ScaledFontDWrite.cpp
blobefa6d823090516232df4ca4a98e5d5f24776d0a3
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "ScaledFontDWrite.h"
7 #include "PathD2D.h"
8 #include "DrawTargetD2D.h"
9 #include "Logging.h"
11 #include <vector>
13 namespace mozilla {
14 namespace gfx {
16 struct ffReferenceKey
18 uint8_t *mData;
19 uint32_t mSize;
22 class DWriteFontFileLoader : public IDWriteFontFileLoader
24 public:
25 DWriteFontFileLoader()
29 // IUnknown interface
30 IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
32 if (iid == __uuidof(IDWriteFontFileLoader)) {
33 *ppObject = static_cast<IDWriteFontFileLoader*>(this);
34 return S_OK;
35 } else if (iid == __uuidof(IUnknown)) {
36 *ppObject = static_cast<IUnknown*>(this);
37 return S_OK;
38 } else {
39 return E_NOINTERFACE;
43 IFACEMETHOD_(ULONG, AddRef)()
45 return 1;
48 IFACEMETHOD_(ULONG, Release)()
50 return 1;
53 // IDWriteFontFileLoader methods
54 /**
55 * Important! Note the key here -has- to be a pointer to an
56 * ffReferenceKey object.
58 virtual HRESULT STDMETHODCALLTYPE
59 CreateStreamFromKey(void const* fontFileReferenceKey,
60 UINT32 fontFileReferenceKeySize,
61 OUT IDWriteFontFileStream** fontFileStream);
63 /**
64 * Gets the singleton loader instance. Note that when using this font
65 * loader, the key must be a pointer to an FallibleTArray<uint8_t>. This
66 * array will be empty when the function returns.
68 static IDWriteFontFileLoader* Instance()
70 if (!mInstance) {
71 mInstance = new DWriteFontFileLoader();
72 DrawTargetD2D::GetDWriteFactory()->
73 RegisterFontFileLoader(mInstance);
75 return mInstance;
78 private:
79 static IDWriteFontFileLoader* mInstance;
80 };
82 class DWriteFontFileStream : public IDWriteFontFileStream
84 public:
85 /**
86 * Used by the FontFileLoader to create a new font stream,
87 * this font stream is created from data in memory. The memory
88 * passed may be released after object creation, it will be
89 * copied internally.
91 * @param aData Font data
93 DWriteFontFileStream(uint8_t *aData, uint32_t aSize);
94 ~DWriteFontFileStream();
96 // IUnknown interface
97 IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
99 if (iid == __uuidof(IDWriteFontFileStream)) {
100 *ppObject = static_cast<IDWriteFontFileStream*>(this);
101 return S_OK;
102 } else if (iid == __uuidof(IUnknown)) {
103 *ppObject = static_cast<IUnknown*>(this);
104 return S_OK;
105 } else {
106 return E_NOINTERFACE;
110 IFACEMETHOD_(ULONG, AddRef)()
112 ++mRefCnt;
113 return mRefCnt;
116 IFACEMETHOD_(ULONG, Release)()
118 --mRefCnt;
119 if (mRefCnt == 0) {
120 delete this;
121 return 0;
123 return mRefCnt;
126 // IDWriteFontFileStream methods
127 virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart,
128 UINT64 fileOffset,
129 UINT64 fragmentSize,
130 OUT void** fragmentContext);
132 virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
134 virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize);
136 virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime);
138 private:
139 std::vector<uint8_t> mData;
140 uint32_t mRefCnt;
143 static BYTE
144 GetSystemTextQuality()
146 BOOL font_smoothing;
147 UINT smoothing_type;
149 if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) {
150 return DEFAULT_QUALITY;
153 if (font_smoothing) {
154 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE,
155 0, &smoothing_type, 0)) {
156 return DEFAULT_QUALITY;
159 if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) {
160 return CLEARTYPE_QUALITY;
163 return ANTIALIASED_QUALITY;
166 return DEFAULT_QUALITY;
169 #define GASP_TAG 0x70736167
170 #define GASP_DOGRAY 0x2
172 static inline unsigned short
173 readShort(const char *aBuf)
175 return (*aBuf << 8) | *(aBuf + 1);
178 static bool
179 DoGrayscale(IDWriteFontFace *aDWFace, Float ppem)
181 void *tableContext;
182 char *tableData;
183 UINT32 tableSize;
184 BOOL exists;
185 aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists);
187 if (exists) {
188 if (tableSize < 4) {
189 aDWFace->ReleaseFontTable(tableContext);
190 return true;
192 struct gaspRange {
193 unsigned short maxPPEM; // Stored big-endian
194 unsigned short behavior; // Stored big-endian
196 unsigned short numRanges = readShort(tableData + 2);
197 if (tableSize < (UINT)4 + numRanges * 4) {
198 aDWFace->ReleaseFontTable(tableContext);
199 return true;
201 gaspRange *ranges = (gaspRange *)(tableData + 4);
202 for (int i = 0; i < numRanges; i++) {
203 if (readShort((char*)&ranges[i].maxPPEM) > ppem) {
204 if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) {
205 aDWFace->ReleaseFontTable(tableContext);
206 return false;
208 break;
211 aDWFace->ReleaseFontTable(tableContext);
213 return true;
216 IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr;
218 HRESULT STDMETHODCALLTYPE
219 DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey,
220 UINT32 fontFileReferenceKeySize,
221 IDWriteFontFileStream **fontFileStream)
223 if (!fontFileReferenceKey || !fontFileStream) {
224 return E_POINTER;
227 const ffReferenceKey *key = static_cast<const ffReferenceKey*>(fontFileReferenceKey);
228 *fontFileStream =
229 new DWriteFontFileStream(key->mData, key->mSize);
231 if (!*fontFileStream) {
232 return E_OUTOFMEMORY;
234 (*fontFileStream)->AddRef();
235 return S_OK;
238 DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize)
239 : mRefCnt(0)
241 mData.resize(aSize);
242 memcpy(&mData.front(), aData, aSize);
245 DWriteFontFileStream::~DWriteFontFileStream()
249 HRESULT STDMETHODCALLTYPE
250 DWriteFontFileStream::GetFileSize(UINT64 *fileSize)
252 *fileSize = mData.size();
253 return S_OK;
256 HRESULT STDMETHODCALLTYPE
257 DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime)
259 return E_NOTIMPL;
262 HRESULT STDMETHODCALLTYPE
263 DWriteFontFileStream::ReadFileFragment(const void **fragmentStart,
264 UINT64 fileOffset,
265 UINT64 fragmentSize,
266 void **fragmentContext)
268 // We are required to do bounds checking.
269 if (fileOffset + fragmentSize > mData.size()) {
270 return E_FAIL;
273 // truncate the 64 bit fileOffset to size_t sized index into mData
274 size_t index = static_cast<size_t>(fileOffset);
276 // We should be alive for the duration of this.
277 *fragmentStart = &mData[index];
278 *fragmentContext = nullptr;
279 return S_OK;
282 void STDMETHODCALLTYPE
283 DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext)
287 ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize,
288 uint32_t aIndex, Float aGlyphSize)
289 : ScaledFontBase(aGlyphSize)
291 IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory();
293 ffReferenceKey key;
294 key.mData = aData;
295 key.mSize = aSize;
297 RefPtr<IDWriteFontFile> fontFile;
298 if (FAILED(factory->CreateCustomFontFileReference(&key, sizeof(ffReferenceKey), DWriteFontFileLoader::Instance(), byRef(fontFile)))) {
299 gfxWarning() << "Failed to load font file from data!";
300 return;
303 IDWriteFontFile *ff = fontFile;
304 if (FAILED(factory->CreateFontFace(DWRITE_FONT_FACE_TYPE_TRUETYPE, 1, &ff, aIndex, DWRITE_FONT_SIMULATIONS_NONE, byRef(mFontFace)))) {
305 gfxWarning() << "Failed to create font face from font file data!";
309 TemporaryRef<Path>
310 ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget)
312 if (aTarget->GetType() != BACKEND_DIRECT2D) {
313 return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
316 RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
318 PathBuilderD2D *pathBuilderD2D =
319 static_cast<PathBuilderD2D*>(pathBuilder.get());
321 CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
323 return pathBuilder->Finish();
326 void
327 ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, const Matrix *)
329 // XXX - Check path builder type!
330 PathBuilderD2D *pathBuilderD2D =
331 static_cast<PathBuilderD2D*>(aBuilder);
333 CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
336 void
337 ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink)
339 std::vector<UINT16> indices;
340 std::vector<FLOAT> advances;
341 std::vector<DWRITE_GLYPH_OFFSET> offsets;
342 indices.resize(aBuffer.mNumGlyphs);
343 advances.resize(aBuffer.mNumGlyphs);
344 offsets.resize(aBuffer.mNumGlyphs);
346 memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs);
347 for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
348 indices[i] = aBuffer.mGlyphs[i].mIndex;
349 offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x;
350 offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y;
353 mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(),
354 &offsets.front(), aBuffer.mNumGlyphs,
355 FALSE, FALSE, aSink);
358 bool
359 ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton)
361 UINT32 fileCount = 0;
362 mFontFace->GetFiles(&fileCount, nullptr);
364 if (fileCount > 1) {
365 MOZ_ASSERT(false);
366 return false;
369 RefPtr<IDWriteFontFile> file;
370 mFontFace->GetFiles(&fileCount, byRef(file));
372 const void *referenceKey;
373 UINT32 refKeySize;
374 // XXX - This can currently crash for webfonts, as when we get the reference
375 // key out of the file, that can be an invalid reference key for the loader
376 // we use it with. The fix to this is not obvious but it will probably
377 // have to happen inside thebes.
378 file->GetReferenceKey(&referenceKey, &refKeySize);
380 RefPtr<IDWriteFontFileLoader> loader;
381 file->GetLoader(byRef(loader));
383 RefPtr<IDWriteFontFileStream> stream;
384 loader->CreateStreamFromKey(referenceKey, refKeySize, byRef(stream));
386 UINT64 fileSize64;
387 stream->GetFileSize(&fileSize64);
388 if (fileSize64 > UINT32_MAX) {
389 MOZ_ASSERT(false);
390 return false;
393 uint32_t fileSize = static_cast<uint32_t>(fileSize64);
394 const void *fragmentStart;
395 void *context;
396 stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context);
398 aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton);
400 stream->ReleaseFileFragment(context);
402 return true;
405 AntialiasMode
406 ScaledFontDWrite::GetDefaultAAMode()
408 AntialiasMode defaultMode = AA_SUBPIXEL;
410 switch (GetSystemTextQuality()) {
411 case CLEARTYPE_QUALITY:
412 defaultMode = AA_SUBPIXEL;
413 break;
414 case ANTIALIASED_QUALITY:
415 defaultMode = AA_GRAY;
416 break;
417 case DEFAULT_QUALITY:
418 defaultMode = AA_NONE;
419 break;
422 if (defaultMode == AA_GRAY) {
423 if (!DoGrayscale(mFontFace, mSize)) {
424 defaultMode = AA_NONE;
427 return defaultMode;