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