1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "ScaledFontDWrite.h"
8 #include "UnscaledFontDWrite.h"
9 #include "PathD2D.h"
10 #include "gfxFont.h"
11 #include "Logging.h"
12 #include "mozilla/FontPropertyTypes.h"
13 #include "mozilla/webrender/WebRenderTypes.h"
14 #include "HelpersD2D.h"
15 #include "StackArray.h"
16
17 #include "dwrite_3.h"
18
19 // Currently, we build with WINVER=0x601 (Win7), which means newer
20 // declarations in dwrite_3.h will not be visible. Also, we don't
21 // yet have the Fall Creators Update SDK available on build machines,
22 // so even with updated WINVER, some of the interfaces we need would
23 // not be present.
24 // To work around this, until the build environment is updated,
25 // we #include an extra header that contains copies of the relevant
26 // classes/interfaces we need.
27 #if !defined(__MINGW32__) && WINVER < 0x0A00
28 # include "dw-extra.h"
29 #endif
30
31 #ifdef USE_SKIA
32 # include "PathSkia.h"
33 # include "skia/include/core/SkPaint.h"
34 # include "skia/include/core/SkPath.h"
35 # include "skia/include/ports/SkTypeface_win.h"
36 #endif
37
38 #include <vector>
39
40 #ifdef USE_CAIRO_SCALED_FONT
41 # include "cairo-win32.h"
42 #endif
43
44 #include "HelpersWinFonts.h"
45
46 namespace mozilla {
47 namespace gfx {
48
49 #define GASP_TAG 0x70736167
50 #define GASP_DOGRAY 0x2
51
readShort(const char * aBuf)52 static inline unsigned short readShort(const char* aBuf) {
53 return (*aBuf << 8) | *(aBuf + 1);
54 }
55
DoGrayscale(IDWriteFontFace * aDWFace,Float ppem)56 static bool DoGrayscale(IDWriteFontFace* aDWFace, Float ppem) {
57 void* tableContext;
58 char* tableData;
59 UINT32 tableSize;
60 BOOL exists;
61 aDWFace->TryGetFontTable(GASP_TAG, (const void**)&tableData, &tableSize,
62 &tableContext, &exists);
63
64 if (exists) {
65 if (tableSize < 4) {
66 aDWFace->ReleaseFontTable(tableContext);
67 return true;
68 }
69 struct gaspRange {
70 unsigned short maxPPEM; // Stored big-endian
71 unsigned short behavior; // Stored big-endian
72 };
73 unsigned short numRanges = readShort(tableData + 2);
74 if (tableSize < (UINT)4 + numRanges * 4) {
75 aDWFace->ReleaseFontTable(tableContext);
76 return true;
77 }
78 gaspRange* ranges = (gaspRange*)(tableData + 4);
79 for (int i = 0; i < numRanges; i++) {
80 if (readShort((char*)&ranges[i].maxPPEM) > ppem) {
81 if (!(readShort((char*)&ranges[i].behavior) & GASP_DOGRAY)) {
82 aDWFace->ReleaseFontTable(tableContext);
83 return false;
84 }
85 break;
86 }
87 }
88 aDWFace->ReleaseFontTable(tableContext);
89 }
90 return true;
91 }
92
DWriteFontStretchFromStretch(FontStretch aStretch)93 static inline DWRITE_FONT_STRETCH DWriteFontStretchFromStretch(
94 FontStretch aStretch) {
95 if (aStretch == FontStretch::UltraCondensed()) {
96 return DWRITE_FONT_STRETCH_ULTRA_CONDENSED;
97 }
98 if (aStretch == FontStretch::ExtraCondensed()) {
99 return DWRITE_FONT_STRETCH_EXTRA_CONDENSED;
100 }
101 if (aStretch == FontStretch::Condensed()) {
102 return DWRITE_FONT_STRETCH_CONDENSED;
103 }
104 if (aStretch == FontStretch::SemiCondensed()) {
105 return DWRITE_FONT_STRETCH_SEMI_CONDENSED;
106 }
107 if (aStretch == FontStretch::Normal()) {
108 return DWRITE_FONT_STRETCH_NORMAL;
109 }
110 if (aStretch == FontStretch::SemiExpanded()) {
111 return DWRITE_FONT_STRETCH_SEMI_EXPANDED;
112 }
113 if (aStretch == FontStretch::Expanded()) {
114 return DWRITE_FONT_STRETCH_EXPANDED;
115 }
116 if (aStretch == FontStretch::ExtraExpanded()) {
117 return DWRITE_FONT_STRETCH_EXTRA_EXPANDED;
118 }
119 if (aStretch == FontStretch::UltraExpanded()) {
120 return DWRITE_FONT_STRETCH_ULTRA_EXPANDED;
121 }
122 return DWRITE_FONT_STRETCH_UNDEFINED;
123 }
124
ScaledFontDWrite(IDWriteFontFace * aFontFace,const RefPtr<UnscaledFont> & aUnscaledFont,Float aSize,bool aUseEmbeddedBitmap,DWRITE_RENDERING_MODE aRenderingMode,IDWriteRenderingParams * aParams,Float aGamma,Float aContrast,Float aClearTypeLevel,const gfxFontStyle * aStyle)125 ScaledFontDWrite::ScaledFontDWrite(
126 IDWriteFontFace* aFontFace, const RefPtr<UnscaledFont>& aUnscaledFont,
127 Float aSize, bool aUseEmbeddedBitmap, DWRITE_RENDERING_MODE aRenderingMode,
128 IDWriteRenderingParams* aParams, Float aGamma, Float aContrast,
129 Float aClearTypeLevel, const gfxFontStyle* aStyle)
130 : ScaledFontBase(aUnscaledFont, aSize),
131 mFontFace(aFontFace),
132 mUseEmbeddedBitmap(aUseEmbeddedBitmap),
133 mRenderingMode(aRenderingMode),
134 mParams(aParams),
135 mGamma(aGamma),
136 mContrast(aContrast),
137 mClearTypeLevel(aClearTypeLevel) {
138 if (aStyle) {
139 mStyle = SkFontStyle(aStyle->weight.ToIntRounded(),
140 DWriteFontStretchFromStretch(aStyle->stretch),
141 // FIXME(jwatt): also use kOblique_Slant
142 aStyle->style == FontSlantStyle::Normal()
143 ? SkFontStyle::kUpright_Slant
144 : SkFontStyle::kItalic_Slant);
145 }
146 }
147
GetPathForGlyphs(const GlyphBuffer & aBuffer,const DrawTarget * aTarget)148 already_AddRefed<Path> ScaledFontDWrite::GetPathForGlyphs(
149 const GlyphBuffer& aBuffer, const DrawTarget* aTarget) {
150 RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
151
152 if (pathBuilder->GetBackendType() != BackendType::DIRECT2D &&
153 pathBuilder->GetBackendType() != BackendType::DIRECT2D1_1) {
154 return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
155 }
156
157 PathBuilderD2D* pathBuilderD2D =
158 static_cast<PathBuilderD2D*>(pathBuilder.get());
159
160 CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
161
162 return pathBuilder->Finish();
163 }
164
165 #ifdef USE_SKIA
CreateSkTypeface()166 SkTypeface* ScaledFontDWrite::CreateSkTypeface() {
167 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
168 if (!factory) {
169 return nullptr;
170 }
171
172 Float gamma = mGamma;
173 // Skia doesn't support a gamma value outside of 0-4, so default to 2.2
174 if (gamma < 0.0f || gamma > 4.0f) {
175 gamma = 2.2f;
176 }
177
178 Float contrast = mContrast;
179 // Skia doesn't support a contrast value outside of 0-1, so default to 1.0
180 if (contrast < 0.0f || contrast > 1.0f) {
181 contrast = 1.0f;
182 }
183
184 Float clearTypeLevel = mClearTypeLevel;
185 if (clearTypeLevel < 0.0f || clearTypeLevel > 1.0f) {
186 clearTypeLevel = 1.0f;
187 }
188
189 return SkCreateTypefaceFromDWriteFont(factory, mFontFace, mStyle,
190 (int)mRenderingMode, gamma, contrast,
191 clearTypeLevel);
192 }
193
SetupSkFontDrawOptions(SkFont & aFont)194 void ScaledFontDWrite::SetupSkFontDrawOptions(SkFont& aFont) {
195 if (ForceGDIMode()) {
196 aFont.setEmbeddedBitmaps(true);
197 aFont.setSubpixel(false);
198 } else {
199 aFont.setEmbeddedBitmaps(UseEmbeddedBitmaps());
200 aFont.setSubpixel(true);
201 }
202 }
203 #endif
204
CopyGlyphsToBuilder(const GlyphBuffer & aBuffer,PathBuilder * aBuilder,const Matrix * aTransformHint)205 void ScaledFontDWrite::CopyGlyphsToBuilder(const GlyphBuffer& aBuffer,
206 PathBuilder* aBuilder,
207 const Matrix* aTransformHint) {
208 BackendType backendType = aBuilder->GetBackendType();
209 if (backendType == BackendType::CAPTURE) {
210 StreamingGeometrySink sink(aBuilder);
211 CopyGlyphsToSink(aBuffer, &sink);
212 return;
213 }
214
215 if (backendType != BackendType::DIRECT2D &&
216 backendType != BackendType::DIRECT2D1_1) {
217 ScaledFontBase::CopyGlyphsToBuilder(aBuffer, aBuilder, aTransformHint);
218 return;
219 }
220
221 PathBuilderD2D* pathBuilderD2D = static_cast<PathBuilderD2D*>(aBuilder);
222
223 if (pathBuilderD2D->IsFigureActive()) {
224 gfxCriticalNote
225 << "Attempting to copy glyphs to PathBuilderD2D with active figure.";
226 }
227
228 CopyGlyphsToSink(aBuffer, pathBuilderD2D->GetSink());
229 }
230
CopyGlyphsToSink(const GlyphBuffer & aBuffer,ID2D1SimplifiedGeometrySink * aSink)231 void ScaledFontDWrite::CopyGlyphsToSink(const GlyphBuffer& aBuffer,
232 ID2D1SimplifiedGeometrySink* aSink) {
233 std::vector<UINT16> indices;
234 std::vector<FLOAT> advances;
235 std::vector<DWRITE_GLYPH_OFFSET> offsets;
236 indices.resize(aBuffer.mNumGlyphs);
237 advances.resize(aBuffer.mNumGlyphs);
238 offsets.resize(aBuffer.mNumGlyphs);
239
240 memset(&advances.front(), 0, sizeof(FLOAT) * aBuffer.mNumGlyphs);
241 for (unsigned int i = 0; i < aBuffer.mNumGlyphs; i++) {
242 indices[i] = aBuffer.mGlyphs[i].mIndex;
243 offsets[i].advanceOffset = aBuffer.mGlyphs[i].mPosition.x;
244 offsets[i].ascenderOffset = -aBuffer.mGlyphs[i].mPosition.y;
245 }
246
247 HRESULT hr = mFontFace->GetGlyphRunOutline(
248 mSize, &indices.front(), &advances.front(), &offsets.front(),
249 aBuffer.mNumGlyphs, FALSE, FALSE, aSink);
250 if (FAILED(hr)) {
251 gfxCriticalNote << "Failed to copy glyphs to geometry sink. Code: "
252 << hexa(hr);
253 }
254 }
255
GetFontFileData(FontFileDataOutput aDataCallback,void * aBaton)256 bool UnscaledFontDWrite::GetFontFileData(FontFileDataOutput aDataCallback,
257 void* aBaton) {
258 UINT32 fileCount = 0;
259 HRESULT hr = mFontFace->GetFiles(&fileCount, nullptr);
260
261 if (FAILED(hr) || fileCount > 1) {
262 MOZ_ASSERT(false);
263 return false;
264 }
265
266 if (!aDataCallback) {
267 return true;
268 }
269
270 RefPtr<IDWriteFontFile> file;
271 hr = mFontFace->GetFiles(&fileCount, getter_AddRefs(file));
272 if (FAILED(hr)) {
273 return false;
274 }
275
276 const void* referenceKey;
277 UINT32 refKeySize;
278 // XXX - This can currently crash for webfonts, as when we get the reference
279 // key out of the file, that can be an invalid reference key for the loader
280 // we use it with. The fix to this is not obvious but it will probably
281 // have to happen inside thebes.
282 hr = file->GetReferenceKey(&referenceKey, &refKeySize);
283 if (FAILED(hr)) {
284 return false;
285 }
286
287 RefPtr<IDWriteFontFileLoader> loader;
288 hr = file->GetLoader(getter_AddRefs(loader));
289 if (FAILED(hr)) {
290 return false;
291 }
292
293 RefPtr<IDWriteFontFileStream> stream;
294 hr = loader->CreateStreamFromKey(referenceKey, refKeySize,
295 getter_AddRefs(stream));
296 if (FAILED(hr)) {
297 return false;
298 }
299
300 UINT64 fileSize64;
301 hr = stream->GetFileSize(&fileSize64);
302 if (FAILED(hr) || fileSize64 > UINT32_MAX) {
303 MOZ_ASSERT(false);
304 return false;
305 }
306
307 // Try to catch any device memory exceptions that may occur while attempting
308 // to read the file fragment.
309 void* context = nullptr;
310 hr = E_FAIL;
311 MOZ_SEH_TRY {
312 uint32_t fileSize = static_cast<uint32_t>(fileSize64);
313 const void* fragmentStart = nullptr;
314 hr = stream->ReadFileFragment(&fragmentStart, 0, fileSize, &context);
315 if (SUCCEEDED(hr)) {
316 aDataCallback((uint8_t*)fragmentStart, fileSize, mFontFace->GetIndex(),
317 aBaton);
318 }
319 }
320 MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) {
321 gfxCriticalNote << "Exception occurred reading DWrite font file data";
322 }
323 if (FAILED(hr)) {
324 return false;
325 }
326 stream->ReleaseFileFragment(context);
327 return true;
328 }
329
GetFontFileName(RefPtr<IDWriteFontFace> aFontFace,std::vector<WCHAR> & aFileName)330 static bool GetFontFileName(RefPtr<IDWriteFontFace> aFontFace,
331 std::vector<WCHAR>& aFileName) {
332 UINT32 numFiles;
333 HRESULT hr = aFontFace->GetFiles(&numFiles, nullptr);
334 if (FAILED(hr)) {
335 gfxDebug() << "Failed getting file count for WR font";
336 return false;
337 } else if (numFiles != 1) {
338 gfxDebug() << "Invalid file count " << numFiles << " for WR font";
339 return false;
340 }
341
342 RefPtr<IDWriteFontFile> file;
343 hr = aFontFace->GetFiles(&numFiles, getter_AddRefs(file));
344 if (FAILED(hr)) {
345 gfxDebug() << "Failed getting file for WR font";
346 return false;
347 }
348
349 const void* key;
350 UINT32 keySize;
351 hr = file->GetReferenceKey(&key, &keySize);
352 if (FAILED(hr)) {
353 gfxDebug() << "Failed getting file ref key for WR font";
354 return false;
355 }
356 RefPtr<IDWriteFontFileLoader> loader;
357 hr = file->GetLoader(getter_AddRefs(loader));
358 if (FAILED(hr)) {
359 gfxDebug() << "Failed getting file loader for WR font";
360 return false;
361 }
362 RefPtr<IDWriteLocalFontFileLoader> localLoader;
363 loader->QueryInterface(__uuidof(IDWriteLocalFontFileLoader),
364 (void**)getter_AddRefs(localLoader));
365 if (!localLoader) {
366 gfxDebug() << "Failed querying loader interface for WR font";
367 return false;
368 }
369 UINT32 pathLen;
370 hr = localLoader->GetFilePathLengthFromKey(key, keySize, &pathLen);
371 if (FAILED(hr)) {
372 gfxDebug() << "Failed getting path length for WR font";
373 return false;
374 }
375 aFileName.resize(pathLen + 1);
376 hr = localLoader->GetFilePathFromKey(key, keySize, aFileName.data(),
377 pathLen + 1);
378 if (FAILED(hr) || aFileName.back() != 0) {
379 gfxDebug() << "Failed getting path for WR font";
380 return false;
381 }
382 DWORD attribs = GetFileAttributesW(aFileName.data());
383 if (attribs == INVALID_FILE_ATTRIBUTES) {
384 gfxDebug() << "Invalid file \"" << aFileName.data() << "\" for WR font";
385 return false;
386 }
387 // We leave the null terminator at the end of the returned file name.
388 return true;
389 }
390
GetFontDescriptor(FontDescriptorOutput aCb,void * aBaton)391 bool UnscaledFontDWrite::GetFontDescriptor(FontDescriptorOutput aCb,
392 void* aBaton) {
393 if (!mFont) {
394 return false;
395 }
396
397 std::vector<WCHAR> fileName;
398 if (!GetFontFileName(mFontFace, fileName)) {
399 return false;
400 }
401 uint32_t index = mFontFace->GetIndex();
402
403 aCb(reinterpret_cast<const uint8_t*>(fileName.data()),
404 fileName.size() * sizeof(WCHAR), index, aBaton);
405 return true;
406 }
407
InstanceData(const wr::FontInstanceOptions * aOptions,const wr::FontInstancePlatformOptions * aPlatformOptions)408 ScaledFontDWrite::InstanceData::InstanceData(
409 const wr::FontInstanceOptions* aOptions,
410 const wr::FontInstancePlatformOptions* aPlatformOptions)
411 : mUseEmbeddedBitmap(false),
412 mApplySyntheticBold(false),
413 mRenderingMode(DWRITE_RENDERING_MODE_DEFAULT),
414 mGamma(2.2f),
415 mContrast(1.0f),
416 mClearTypeLevel(1.0f) {
417 if (aOptions) {
418 if (aOptions->flags & wr::FontInstanceFlags::EMBEDDED_BITMAPS) {
419 mUseEmbeddedBitmap = true;
420 }
421 if (aOptions->flags & wr::FontInstanceFlags::SYNTHETIC_BOLD) {
422 mApplySyntheticBold = true;
423 }
424 if (aOptions->flags & wr::FontInstanceFlags::FORCE_GDI) {
425 mRenderingMode = DWRITE_RENDERING_MODE_GDI_CLASSIC;
426 } else if (aOptions->flags & wr::FontInstanceFlags::FORCE_SYMMETRIC) {
427 mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
428 } else if (aOptions->flags & wr::FontInstanceFlags::NO_SYMMETRIC) {
429 mRenderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
430 }
431 }
432 if (aPlatformOptions) {
433 mGamma = aPlatformOptions->gamma / 100.0f;
434 mContrast = aPlatformOptions->contrast / 100.0f;
435 mClearTypeLevel = aPlatformOptions->cleartype_level / 100.0f;
436 }
437 }
438
439 // Helper for ScaledFontDWrite::GetFontInstanceData: if the font has variation
440 // axes, get their current values into the aOutput vector.
GetVariationsFromFontFace(IDWriteFontFace * aFace,std::vector<FontVariation> * aOutput)441 static void GetVariationsFromFontFace(IDWriteFontFace* aFace,
442 std::vector<FontVariation>* aOutput) {
443 RefPtr<IDWriteFontFace5> ff5;
444 aFace->QueryInterface(__uuidof(IDWriteFontFace5),
445 (void**)getter_AddRefs(ff5));
446 if (!ff5 || !ff5->HasVariations()) {
447 return;
448 }
449
450 uint32_t count = ff5->GetFontAxisValueCount();
451 if (!count) {
452 return;
453 }
454
455 RefPtr<IDWriteFontResource> res;
456 if (FAILED(ff5->GetFontResource(getter_AddRefs(res)))) {
457 return;
458 }
459
460 std::vector<DWRITE_FONT_AXIS_VALUE> values(count);
461 if (FAILED(ff5->GetFontAxisValues(values.data(), count))) {
462 return;
463 }
464
465 aOutput->reserve(count);
466 for (uint32_t i = 0; i < count; i++) {
467 DWRITE_FONT_AXIS_ATTRIBUTES attr = res->GetFontAxisAttributes(i);
468 if (attr & DWRITE_FONT_AXIS_ATTRIBUTES_VARIABLE) {
469 float v = values[i].value;
470 uint32_t t = TRUETYPE_TAG(
471 uint8_t(values[i].axisTag), uint8_t(values[i].axisTag >> 8),
472 uint8_t(values[i].axisTag >> 16), uint8_t(values[i].axisTag >> 24));
473 aOutput->push_back(FontVariation{uint32_t(t), float(v)});
474 }
475 }
476 }
477
GetFontInstanceData(FontInstanceDataOutput aCb,void * aBaton)478 bool ScaledFontDWrite::GetFontInstanceData(FontInstanceDataOutput aCb,
479 void* aBaton) {
480 InstanceData instance(this);
481
482 // If the font has variations, get the list of axis values.
483 std::vector<FontVariation> variations;
484 GetVariationsFromFontFace(mFontFace, &variations);
485
486 aCb(reinterpret_cast<uint8_t*>(&instance), sizeof(instance),
487 variations.data(), variations.size(), aBaton);
488
489 return true;
490 }
491
GetWRFontInstanceOptions(Maybe<wr::FontInstanceOptions> * aOutOptions,Maybe<wr::FontInstancePlatformOptions> * aOutPlatformOptions,std::vector<FontVariation> * aOutVariations)492 bool ScaledFontDWrite::GetWRFontInstanceOptions(
493 Maybe<wr::FontInstanceOptions>* aOutOptions,
494 Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
495 std::vector<FontVariation>* aOutVariations) {
496 wr::FontInstanceOptions options;
497 options.render_mode = wr::ToFontRenderMode(GetDefaultAAMode());
498 options.flags = wr::FontInstanceFlags{0};
499 if (HasSyntheticBold()) {
500 options.flags |= wr::FontInstanceFlags::SYNTHETIC_BOLD;
501 }
502 if (UseEmbeddedBitmaps()) {
503 options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS;
504 }
505 if (ForceGDIMode()) {
506 options.flags |= wr::FontInstanceFlags::FORCE_GDI;
507 } else {
508 options.flags |= wr::FontInstanceFlags::SUBPIXEL_POSITION;
509 }
510 switch (GetRenderingMode()) {
511 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:
512 options.flags |= wr::FontInstanceFlags::FORCE_SYMMETRIC;
513 break;
514 case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:
515 options.flags |= wr::FontInstanceFlags::NO_SYMMETRIC;
516 break;
517 default:
518 break;
519 }
520 if (Factory::GetBGRSubpixelOrder()) {
521 options.flags |= wr::FontInstanceFlags::SUBPIXEL_BGR;
522 }
523 options.bg_color = wr::ToColorU(DeviceColor());
524 options.synthetic_italics =
525 wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());
526
527 wr::FontInstancePlatformOptions platformOptions;
528 platformOptions.gamma = uint16_t(std::round(mGamma * 100.0f));
529 platformOptions.contrast =
530 uint8_t(std::round(std::min(mContrast, 1.0f) * 100.0f));
531 platformOptions.cleartype_level =
532 uint8_t(std::round(std::min(mClearTypeLevel, 1.0f) * 100.0f));
533
534 *aOutOptions = Some(options);
535 *aOutPlatformOptions = Some(platformOptions);
536
537 GetVariationsFromFontFace(mFontFace, aOutVariations);
538
539 return true;
540 }
541
542 // Helper for UnscaledFontDWrite::CreateScaledFont: create a clone of the
543 // given IDWriteFontFace, with specified variation-axis values applied.
544 // Returns nullptr in case of failure.
CreateFaceWithVariations(IDWriteFontFace * aFace,DWRITE_FONT_SIMULATIONS aSimulations,const FontVariation * aVariations=nullptr,uint32_t aNumVariations=0)545 static already_AddRefed<IDWriteFontFace5> CreateFaceWithVariations(
546 IDWriteFontFace* aFace, DWRITE_FONT_SIMULATIONS aSimulations,
547 const FontVariation* aVariations = nullptr, uint32_t aNumVariations = 0) {
548 auto makeDWriteAxisTag = [](uint32_t aTag) {
549 return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff, (aTag >> 16) & 0xff,
550 (aTag >> 8) & 0xff, aTag & 0xff);
551 };
552
553 RefPtr<IDWriteFontFace5> ff5;
554 aFace->QueryInterface(__uuidof(IDWriteFontFace5),
555 (void**)getter_AddRefs(ff5));
556 if (!ff5) {
557 return nullptr;
558 }
559
560 RefPtr<IDWriteFontResource> res;
561 if (FAILED(ff5->GetFontResource(getter_AddRefs(res)))) {
562 return nullptr;
563 }
564
565 std::vector<DWRITE_FONT_AXIS_VALUE> fontAxisValues;
566 if (aNumVariations) {
567 fontAxisValues.reserve(aNumVariations);
568 for (uint32_t i = 0; i < aNumVariations; i++) {
569 DWRITE_FONT_AXIS_VALUE axisValue = {
570 makeDWriteAxisTag(aVariations[i].mTag), aVariations[i].mValue};
571 fontAxisValues.push_back(axisValue);
572 }
573 } else {
574 uint32_t count = ff5->GetFontAxisValueCount();
575 if (count) {
576 fontAxisValues.resize(count);
577 if (FAILED(ff5->GetFontAxisValues(fontAxisValues.data(), count))) {
578 fontAxisValues.clear();
579 }
580 }
581 }
582
583 RefPtr<IDWriteFontFace5> newFace;
584 if (FAILED(res->CreateFontFace(aSimulations, fontAxisValues.data(),
585 fontAxisValues.size(),
586 getter_AddRefs(newFace)))) {
587 return nullptr;
588 }
589
590 return newFace.forget();
591 }
592
InitBold()593 bool UnscaledFontDWrite::InitBold() {
594 if (mFontFaceBold) {
595 return true;
596 }
597
598 DWRITE_FONT_SIMULATIONS sims = mFontFace->GetSimulations();
599 if (sims & DWRITE_FONT_SIMULATIONS_BOLD) {
600 mFontFaceBold = mFontFace;
601 return true;
602 }
603 sims |= DWRITE_FONT_SIMULATIONS_BOLD;
604
605 RefPtr<IDWriteFontFace5> ff5 = CreateFaceWithVariations(mFontFace, sims);
606 if (ff5) {
607 mFontFaceBold = ff5;
608 } else {
609 UINT32 numFiles = 0;
610 if (FAILED(mFontFace->GetFiles(&numFiles, nullptr))) {
611 return false;
612 }
613 StackArray<IDWriteFontFile*, 1> files(numFiles);
614 if (FAILED(mFontFace->GetFiles(&numFiles, files.data()))) {
615 return false;
616 }
617 HRESULT hr = Factory::GetDWriteFactory()->CreateFontFace(
618 mFontFace->GetType(), numFiles, files.data(), mFontFace->GetIndex(),
619 sims, getter_AddRefs(mFontFaceBold));
620 for (UINT32 i = 0; i < numFiles; ++i) {
621 files[i]->Release();
622 }
623 if (FAILED(hr) || !mFontFaceBold) {
624 return false;
625 }
626 }
627 return true;
628 }
629
CreateScaledFont(Float aGlyphSize,const uint8_t * aInstanceData,uint32_t aInstanceDataLength,const FontVariation * aVariations,uint32_t aNumVariations)630 already_AddRefed<ScaledFont> UnscaledFontDWrite::CreateScaledFont(
631 Float aGlyphSize, const uint8_t* aInstanceData,
632 uint32_t aInstanceDataLength, const FontVariation* aVariations,
633 uint32_t aNumVariations) {
634 if (aInstanceDataLength < sizeof(ScaledFontDWrite::InstanceData)) {
635 gfxWarning() << "DWrite scaled font instance data is truncated.";
636 return nullptr;
637 }
638 const ScaledFontDWrite::InstanceData& instanceData =
639 *reinterpret_cast<const ScaledFontDWrite::InstanceData*>(aInstanceData);
640
641 IDWriteFontFace* face = mFontFace;
642 if (instanceData.mApplySyntheticBold) {
643 if (!InitBold()) {
644 gfxWarning() << "Failed creating bold IDWriteFontFace.";
645 return nullptr;
646 }
647 face = mFontFaceBold;
648 }
649 DWRITE_FONT_SIMULATIONS sims = face->GetSimulations();
650
651 // If variations are required, we create a separate IDWriteFontFace5 with
652 // the requested settings applied.
653 RefPtr<IDWriteFontFace5> ff5;
654 if (aNumVariations) {
655 ff5 =
656 CreateFaceWithVariations(mFontFace, sims, aVariations, aNumVariations);
657 if (ff5) {
658 face = ff5;
659 } else {
660 gfxWarning() << "Failed to create IDWriteFontFace5 with variations.";
661 }
662 }
663
664 RefPtr<ScaledFontBase> scaledFont = new ScaledFontDWrite(
665 face, this, aGlyphSize, instanceData.mUseEmbeddedBitmap,
666 instanceData.mRenderingMode, nullptr, instanceData.mGamma,
667 instanceData.mContrast, instanceData.mClearTypeLevel);
668
669 return scaledFont.forget();
670 }
671
CreateScaledFontFromWRFont(Float aGlyphSize,const wr::FontInstanceOptions * aOptions,const wr::FontInstancePlatformOptions * aPlatformOptions,const FontVariation * aVariations,uint32_t aNumVariations)672 already_AddRefed<ScaledFont> UnscaledFontDWrite::CreateScaledFontFromWRFont(
673 Float aGlyphSize, const wr::FontInstanceOptions* aOptions,
674 const wr::FontInstancePlatformOptions* aPlatformOptions,
675 const FontVariation* aVariations, uint32_t aNumVariations) {
676 ScaledFontDWrite::InstanceData instanceData(aOptions, aPlatformOptions);
677 return CreateScaledFont(aGlyphSize, reinterpret_cast<uint8_t*>(&instanceData),
678 sizeof(instanceData), aVariations, aNumVariations);
679 }
680
GetDefaultAAMode()681 AntialiasMode ScaledFontDWrite::GetDefaultAAMode() {
682 AntialiasMode defaultMode = GetSystemDefaultAAMode();
683
684 switch (defaultMode) {
685 case AntialiasMode::SUBPIXEL:
686 case AntialiasMode::DEFAULT:
687 if (mClearTypeLevel == 0.0f) {
688 defaultMode = AntialiasMode::GRAY;
689 }
690 break;
691 case AntialiasMode::GRAY:
692 if (!DoGrayscale(mFontFace, mSize)) {
693 defaultMode = AntialiasMode::NONE;
694 }
695 break;
696 case AntialiasMode::NONE:
697 break;
698 }
699
700 return defaultMode;
701 }
702
703 #ifdef USE_CAIRO_SCALED_FONT
CreateCairoFontFace(cairo_font_options_t * aFontOptions)704 cairo_font_face_t* ScaledFontDWrite::CreateCairoFontFace(
705 cairo_font_options_t* aFontOptions) {
706 if (!mFontFace) {
707 return nullptr;
708 }
709
710 return cairo_dwrite_font_face_create_for_dwrite_fontface(nullptr, mFontFace);
711 }
712
PrepareCairoScaledFont(cairo_scaled_font_t * aFont)713 void ScaledFontDWrite::PrepareCairoScaledFont(cairo_scaled_font_t* aFont) {
714 if (mRenderingMode == DWRITE_RENDERING_MODE_GDI_CLASSIC) {
715 cairo_dwrite_scaled_font_set_force_GDI_classic(aFont, true);
716 }
717 }
718 #endif
719
CreateFromFontDescriptor(const uint8_t * aData,uint32_t aDataLength,uint32_t aIndex)720 already_AddRefed<UnscaledFont> UnscaledFontDWrite::CreateFromFontDescriptor(
721 const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) {
722 if (aDataLength == 0) {
723 gfxWarning() << "DWrite font descriptor is truncated.";
724 return nullptr;
725 }
726
727 RefPtr<IDWriteFactory> factory = Factory::GetDWriteFactory();
728 if (!factory) {
729 return nullptr;
730 }
731 RefPtr<IDWriteFontFile> fontFile;
732 HRESULT hr = factory->CreateFontFileReference((const WCHAR*)aData, nullptr,
733 getter_AddRefs(fontFile));
734 if (FAILED(hr)) {
735 return nullptr;
736 }
737 BOOL isSupported;
738 DWRITE_FONT_FILE_TYPE fileType;
739 DWRITE_FONT_FACE_TYPE faceType;
740 UINT32 numFaces;
741 hr = fontFile->Analyze(&isSupported, &fileType, &faceType, &numFaces);
742 if (FAILED(hr) || !isSupported || aIndex >= numFaces) {
743 return nullptr;
744 }
745 IDWriteFontFile* fontFiles[1] = {fontFile.get()};
746 RefPtr<IDWriteFontFace> fontFace;
747 hr = factory->CreateFontFace(faceType, 1, fontFiles, aIndex,
748 DWRITE_FONT_SIMULATIONS_NONE,
749 getter_AddRefs(fontFace));
750 if (FAILED(hr)) {
751 return nullptr;
752 }
753 RefPtr<UnscaledFont> unscaledFont = new UnscaledFontDWrite(fontFace, nullptr);
754 return unscaledFont.forget();
755 }
756
757 } // namespace gfx
758 } // namespace mozilla
759