|
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/. */ |
|
5 |
|
6 #include "ScaledFontDWrite.h" |
|
7 #include "PathD2D.h" |
|
8 #include "DrawTargetD2D.h" |
|
9 #include "Logging.h" |
|
10 |
|
11 #include <vector> |
|
12 |
|
13 namespace mozilla { |
|
14 namespace gfx { |
|
15 |
|
16 struct ffReferenceKey |
|
17 { |
|
18 uint8_t *mData; |
|
19 uint32_t mSize; |
|
20 }; |
|
21 |
|
22 class DWriteFontFileLoader : public IDWriteFontFileLoader |
|
23 { |
|
24 public: |
|
25 DWriteFontFileLoader() |
|
26 { |
|
27 } |
|
28 |
|
29 // IUnknown interface |
|
30 IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) |
|
31 { |
|
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; |
|
40 } |
|
41 } |
|
42 |
|
43 IFACEMETHOD_(ULONG, AddRef)() |
|
44 { |
|
45 return 1; |
|
46 } |
|
47 |
|
48 IFACEMETHOD_(ULONG, Release)() |
|
49 { |
|
50 return 1; |
|
51 } |
|
52 |
|
53 // IDWriteFontFileLoader methods |
|
54 /** |
|
55 * Important! Note the key here -has- to be a pointer to an |
|
56 * ffReferenceKey object. |
|
57 */ |
|
58 virtual HRESULT STDMETHODCALLTYPE |
|
59 CreateStreamFromKey(void const* fontFileReferenceKey, |
|
60 UINT32 fontFileReferenceKeySize, |
|
61 OUT IDWriteFontFileStream** fontFileStream); |
|
62 |
|
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. |
|
67 */ |
|
68 static IDWriteFontFileLoader* Instance() |
|
69 { |
|
70 if (!mInstance) { |
|
71 mInstance = new DWriteFontFileLoader(); |
|
72 DrawTargetD2D::GetDWriteFactory()-> |
|
73 RegisterFontFileLoader(mInstance); |
|
74 } |
|
75 return mInstance; |
|
76 } |
|
77 |
|
78 private: |
|
79 static IDWriteFontFileLoader* mInstance; |
|
80 }; |
|
81 |
|
82 class DWriteFontFileStream : public IDWriteFontFileStream |
|
83 { |
|
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. |
|
90 * |
|
91 * @param aData Font data |
|
92 */ |
|
93 DWriteFontFileStream(uint8_t *aData, uint32_t aSize); |
|
94 ~DWriteFontFileStream(); |
|
95 |
|
96 // IUnknown interface |
|
97 IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) |
|
98 { |
|
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; |
|
107 } |
|
108 } |
|
109 |
|
110 IFACEMETHOD_(ULONG, AddRef)() |
|
111 { |
|
112 ++mRefCnt; |
|
113 return mRefCnt; |
|
114 } |
|
115 |
|
116 IFACEMETHOD_(ULONG, Release)() |
|
117 { |
|
118 --mRefCnt; |
|
119 if (mRefCnt == 0) { |
|
120 delete this; |
|
121 return 0; |
|
122 } |
|
123 return mRefCnt; |
|
124 } |
|
125 |
|
126 // IDWriteFontFileStream methods |
|
127 virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(void const** fragmentStart, |
|
128 UINT64 fileOffset, |
|
129 UINT64 fragmentSize, |
|
130 OUT void** fragmentContext); |
|
131 |
|
132 virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext); |
|
133 |
|
134 virtual HRESULT STDMETHODCALLTYPE GetFileSize(OUT UINT64* fileSize); |
|
135 |
|
136 virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(OUT UINT64* lastWriteTime); |
|
137 |
|
138 private: |
|
139 std::vector<uint8_t> mData; |
|
140 uint32_t mRefCnt; |
|
141 }; |
|
142 |
|
143 static BYTE |
|
144 GetSystemTextQuality() |
|
145 { |
|
146 BOOL font_smoothing; |
|
147 UINT smoothing_type; |
|
148 |
|
149 if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &font_smoothing, 0)) { |
|
150 return DEFAULT_QUALITY; |
|
151 } |
|
152 |
|
153 if (font_smoothing) { |
|
154 if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, |
|
155 0, &smoothing_type, 0)) { |
|
156 return DEFAULT_QUALITY; |
|
157 } |
|
158 |
|
159 if (smoothing_type == FE_FONTSMOOTHINGCLEARTYPE) { |
|
160 return CLEARTYPE_QUALITY; |
|
161 } |
|
162 |
|
163 return ANTIALIASED_QUALITY; |
|
164 } |
|
165 |
|
166 return DEFAULT_QUALITY; |
|
167 } |
|
168 |
|
169 #define GASP_TAG 0x70736167 |
|
170 #define GASP_DOGRAY 0x2 |
|
171 |
|
172 static inline unsigned short |
|
173 readShort(const char *aBuf) |
|
174 { |
|
175 return (*aBuf << 8) | *(aBuf + 1); |
|
176 } |
|
177 |
|
178 static bool |
|
179 DoGrayscale(IDWriteFontFace *aDWFace, Float ppem) |
|
180 { |
|
181 void *tableContext; |
|
182 char *tableData; |
|
183 UINT32 tableSize; |
|
184 BOOL exists; |
|
185 aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize, &tableContext, &exists); |
|
186 |
|
187 if (exists) { |
|
188 if (tableSize < 4) { |
|
189 aDWFace->ReleaseFontTable(tableContext); |
|
190 return true; |
|
191 } |
|
192 struct gaspRange { |
|
193 unsigned short maxPPEM; // Stored big-endian |
|
194 unsigned short behavior; // Stored big-endian |
|
195 }; |
|
196 unsigned short numRanges = readShort(tableData + 2); |
|
197 if (tableSize < (UINT)4 + numRanges * 4) { |
|
198 aDWFace->ReleaseFontTable(tableContext); |
|
199 return true; |
|
200 } |
|
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; |
|
207 } |
|
208 break; |
|
209 } |
|
210 } |
|
211 aDWFace->ReleaseFontTable(tableContext); |
|
212 } |
|
213 return true; |
|
214 } |
|
215 |
|
216 IDWriteFontFileLoader* DWriteFontFileLoader::mInstance = nullptr; |
|
217 |
|
218 HRESULT STDMETHODCALLTYPE |
|
219 DWriteFontFileLoader::CreateStreamFromKey(const void *fontFileReferenceKey, |
|
220 UINT32 fontFileReferenceKeySize, |
|
221 IDWriteFontFileStream **fontFileStream) |
|
222 { |
|
223 if (!fontFileReferenceKey || !fontFileStream) { |
|
224 return E_POINTER; |
|
225 } |
|
226 |
|
227 const ffReferenceKey *key = static_cast<const ffReferenceKey*>(fontFileReferenceKey); |
|
228 *fontFileStream = |
|
229 new DWriteFontFileStream(key->mData, key->mSize); |
|
230 |
|
231 if (!*fontFileStream) { |
|
232 return E_OUTOFMEMORY; |
|
233 } |
|
234 (*fontFileStream)->AddRef(); |
|
235 return S_OK; |
|
236 } |
|
237 |
|
238 DWriteFontFileStream::DWriteFontFileStream(uint8_t *aData, uint32_t aSize) |
|
239 : mRefCnt(0) |
|
240 { |
|
241 mData.resize(aSize); |
|
242 memcpy(&mData.front(), aData, aSize); |
|
243 } |
|
244 |
|
245 DWriteFontFileStream::~DWriteFontFileStream() |
|
246 { |
|
247 } |
|
248 |
|
249 HRESULT STDMETHODCALLTYPE |
|
250 DWriteFontFileStream::GetFileSize(UINT64 *fileSize) |
|
251 { |
|
252 *fileSize = mData.size(); |
|
253 return S_OK; |
|
254 } |
|
255 |
|
256 HRESULT STDMETHODCALLTYPE |
|
257 DWriteFontFileStream::GetLastWriteTime(UINT64 *lastWriteTime) |
|
258 { |
|
259 return E_NOTIMPL; |
|
260 } |
|
261 |
|
262 HRESULT STDMETHODCALLTYPE |
|
263 DWriteFontFileStream::ReadFileFragment(const void **fragmentStart, |
|
264 UINT64 fileOffset, |
|
265 UINT64 fragmentSize, |
|
266 void **fragmentContext) |
|
267 { |
|
268 // We are required to do bounds checking. |
|
269 if (fileOffset + fragmentSize > mData.size()) { |
|
270 return E_FAIL; |
|
271 } |
|
272 |
|
273 // truncate the 64 bit fileOffset to size_t sized index into mData |
|
274 size_t index = static_cast<size_t>(fileOffset); |
|
275 |
|
276 // We should be alive for the duration of this. |
|
277 *fragmentStart = &mData[index]; |
|
278 *fragmentContext = nullptr; |
|
279 return S_OK; |
|
280 } |
|
281 |
|
282 void STDMETHODCALLTYPE |
|
283 DWriteFontFileStream::ReleaseFileFragment(void *fragmentContext) |
|
284 { |
|
285 } |
|
286 |
|
287 ScaledFontDWrite::ScaledFontDWrite(uint8_t *aData, uint32_t aSize, |
|
288 uint32_t aIndex, Float aGlyphSize) |
|
289 : ScaledFontBase(aGlyphSize) |
|
290 { |
|
291 IDWriteFactory *factory = DrawTargetD2D::GetDWriteFactory(); |
|
292 |
|
293 ffReferenceKey key; |
|
294 key.mData = aData; |
|
295 key.mSize = aSize; |
|
296 |
|
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; |
|
301 } |
|
302 |
|
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!"; |
|
306 } |
|
307 } |
|
308 |
|
309 TemporaryRef<Path> |
|
310 ScaledFontDWrite::GetPathForGlyphs(const GlyphBuffer &aBuffer, const DrawTarget *aTarget) |
|
311 { |
|
312 if (aTarget->GetType() != BackendType::DIRECT2D) { |
|
313 return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget); |
|
314 } |
|
315 |
|
316 RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder(); |
|
317 |
|
318 PathBuilderD2D *pathBuilderD2D = |
|
319 static_cast<PathBuilderD2D*>(pathBuilder.get()); |
|
320 |
|
321 CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); |
|
322 |
|
323 return pathBuilder->Finish(); |
|
324 } |
|
325 |
|
326 void |
|
327 ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer &aBuffer, PathBuilder *aBuilder, BackendType aBackendType, const Matrix *aTransformHint) |
|
328 { |
|
329 if (aBackendType != BackendType::DIRECT2D) { |
|
330 ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aBackendType, aTransformHint); |
|
331 return; |
|
332 } |
|
333 |
|
334 PathBuilderD2D *pathBuilderD2D = |
|
335 static_cast<PathBuilderD2D*>(aBuilder); |
|
336 |
|
337 CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink()); |
|
338 } |
|
339 |
|
340 void |
|
341 ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer &aBuffer, ID2D1GeometrySink *aSink) |
|
342 { |
|
343 std::vector<UINT16> indices; |
|
344 std::vector<FLOAT> advances; |
|
345 std::vector<DWRITE_GLYPH_OFFSET> offsets; |
|
346 indices.resize(aBuffer.mNumGlyphs); |
|
347 advances.resize(aBuffer.mNumGlyphs); |
|
348 offsets.resize(aBuffer.mNumGlyphs); |
|
349 |
|
350 memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs); |
|
351 for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) { |
|
352 indices[i] = aBuffer.mGlyphs[i].mIndex; |
|
353 offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x; |
|
354 offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y; |
|
355 } |
|
356 |
|
357 mFontFace->GetGlyphRunOutline(mSize, &indices.front(), &advances.front(), |
|
358 &offsets.front(), aBuffer.mNumGlyphs, |
|
359 FALSE, FALSE, aSink); |
|
360 } |
|
361 |
|
362 bool |
|
363 ScaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback, void *aBaton) |
|
364 { |
|
365 UINT32 fileCount = 0; |
|
366 mFontFace->GetFiles(&fileCount, nullptr); |
|
367 |
|
368 if (fileCount > 1) { |
|
369 MOZ_ASSERT(false); |
|
370 return false; |
|
371 } |
|
372 |
|
373 RefPtr<IDWriteFontFile> file; |
|
374 mFontFace->GetFiles(&fileCount, byRef(file)); |
|
375 |
|
376 const void *referenceKey; |
|
377 UINT32 refKeySize; |
|
378 // XXX - This can currently crash for webfonts, as when we get the reference |
|
379 // key out of the file, that can be an invalid reference key for the loader |
|
380 // we use it with. The fix to this is not obvious but it will probably |
|
381 // have to happen inside thebes. |
|
382 file->GetReferenceKey(&referenceKey, &refKeySize); |
|
383 |
|
384 RefPtr<IDWriteFontFileLoader> loader; |
|
385 file->GetLoader(byRef(loader)); |
|
386 |
|
387 RefPtr<IDWriteFontFileStream> stream; |
|
388 loader->CreateStreamFromKey(referenceKey, refKeySize, byRef(stream)); |
|
389 |
|
390 UINT64 fileSize64; |
|
391 stream->GetFileSize(&fileSize64); |
|
392 if (fileSize64 > UINT32_MAX) { |
|
393 MOZ_ASSERT(false); |
|
394 return false; |
|
395 } |
|
396 |
|
397 uint32_t fileSize = static_cast<uint32_t>(fileSize64); |
|
398 const void *fragmentStart; |
|
399 void *context; |
|
400 stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context); |
|
401 |
|
402 aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(), mSize, aBaton); |
|
403 |
|
404 stream->ReleaseFileFragment(context); |
|
405 |
|
406 return true; |
|
407 } |
|
408 |
|
409 AntialiasMode |
|
410 ScaledFontDWrite::GetDefaultAAMode() |
|
411 { |
|
412 AntialiasMode defaultMode = AntialiasMode::SUBPIXEL; |
|
413 |
|
414 switch (GetSystemTextQuality()) { |
|
415 case CLEARTYPE_QUALITY: |
|
416 defaultMode = AntialiasMode::SUBPIXEL; |
|
417 break; |
|
418 case ANTIALIASED_QUALITY: |
|
419 defaultMode = AntialiasMode::GRAY; |
|
420 break; |
|
421 case DEFAULT_QUALITY: |
|
422 defaultMode = AntialiasMode::NONE; |
|
423 break; |
|
424 } |
|
425 |
|
426 if (defaultMode == AntialiasMode::GRAY) { |
|
427 if (!DoGrayscale(mFontFace, mSize)) { |
|
428 defaultMode = AntialiasMode::NONE; |
|
429 } |
|
430 } |
|
431 return defaultMode; |
|
432 } |
|
433 |
|
434 } |
|
435 } |