1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 "gfxDWriteFonts.h"
7
8 #include "mozilla/MemoryReporting.h"
9
10 #include <algorithm>
11 #include "gfxDWriteFontList.h"
12 #include "gfxContext.h"
13 #include "gfxTextRun.h"
14 #include <dwrite.h>
15
16 #include "harfbuzz/hb.h"
17
18 using namespace mozilla;
19 using namespace mozilla::gfx;
20
21 // This is also in gfxGDIFont.cpp. Would be nice to put it somewhere common,
22 // but we can't declare it in the gfxFont.h or gfxFontUtils.h headers
23 // because those are exported, and the cairo headers aren't.
24 static inline cairo_antialias_t
GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)25 GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption)
26 {
27 switch (anAntialiasOption) {
28 default:
29 case gfxFont::kAntialiasDefault:
30 return CAIRO_ANTIALIAS_DEFAULT;
31 case gfxFont::kAntialiasNone:
32 return CAIRO_ANTIALIAS_NONE;
33 case gfxFont::kAntialiasGrayscale:
34 return CAIRO_ANTIALIAS_GRAY;
35 case gfxFont::kAntialiasSubpixel:
36 return CAIRO_ANTIALIAS_SUBPIXEL;
37 }
38 }
39
40 // Code to determine whether Windows is set to use ClearType font smoothing;
41 // based on private functions in cairo-win32-font.c
42
43 #ifndef SPI_GETFONTSMOOTHINGTYPE
44 #define SPI_GETFONTSMOOTHINGTYPE 0x200a
45 #endif
46 #ifndef FE_FONTSMOOTHINGCLEARTYPE
47 #define FE_FONTSMOOTHINGCLEARTYPE 2
48 #endif
49
50 static bool
UsingClearType()51 UsingClearType()
52 {
53 BOOL fontSmoothing;
54 if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) ||
55 !fontSmoothing)
56 {
57 return false;
58 }
59
60 UINT type;
61 if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) &&
62 type == FE_FONTSMOOTHINGCLEARTYPE)
63 {
64 return true;
65 }
66 return false;
67 }
68
69 ////////////////////////////////////////////////////////////////////////////////
70 // gfxDWriteFont
gfxDWriteFont(gfxFontEntry * aFontEntry,const gfxFontStyle * aFontStyle,bool aNeedsBold,AntialiasOption anAAOption)71 gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
72 const gfxFontStyle *aFontStyle,
73 bool aNeedsBold,
74 AntialiasOption anAAOption)
75 : gfxFont(aFontEntry, aFontStyle, anAAOption)
76 , mCairoFontFace(nullptr)
77 , mMetrics(nullptr)
78 , mSpaceGlyph(0)
79 , mNeedsOblique(false)
80 , mNeedsBold(aNeedsBold)
81 , mUseSubpixelPositions(false)
82 , mAllowManualShowGlyphs(true)
83 {
84 gfxDWriteFontEntry *fe =
85 static_cast<gfxDWriteFontEntry*>(aFontEntry);
86 nsresult rv;
87 DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE;
88 if ((GetStyle()->style != NS_FONT_STYLE_NORMAL) &&
89 fe->IsUpright() &&
90 GetStyle()->allowSyntheticStyle) {
91 // For this we always use the font_matrix for uniformity. Not the
92 // DWrite simulation.
93 mNeedsOblique = true;
94 }
95 if (aNeedsBold) {
96 sims |= DWRITE_FONT_SIMULATIONS_BOLD;
97 }
98
99 rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims);
100
101 if (NS_FAILED(rv)) {
102 mIsValid = false;
103 return;
104 }
105
106 ComputeMetrics(anAAOption);
107 }
108
~gfxDWriteFont()109 gfxDWriteFont::~gfxDWriteFont()
110 {
111 if (mCairoFontFace) {
112 cairo_font_face_destroy(mCairoFontFace);
113 }
114 if (mScaledFont) {
115 cairo_scaled_font_destroy(mScaledFont);
116 }
117 delete mMetrics;
118 }
119
120 gfxFont*
CopyWithAntialiasOption(AntialiasOption anAAOption)121 gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption)
122 {
123 return new gfxDWriteFont(static_cast<gfxDWriteFontEntry*>(mFontEntry.get()),
124 &mStyle, mNeedsBold, anAAOption);
125 }
126
127 const gfxFont::Metrics&
GetHorizontalMetrics()128 gfxDWriteFont::GetHorizontalMetrics()
129 {
130 return *mMetrics;
131 }
132
133 bool
GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS * aFontMetrics)134 gfxDWriteFont::GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics)
135 {
136 gfxFontStyle style(mStyle);
137 style.weight = 700;
138 bool needsBold;
139
140 gfxFontEntry* fe =
141 gfxPlatformFontList::PlatformFontList()->
142 FindFontForFamily(NS_LITERAL_STRING("Arial"), &style, needsBold);
143 if (!fe || fe == mFontEntry) {
144 return false;
145 }
146
147 RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, needsBold);
148 gfxDWriteFont *dwFont = static_cast<gfxDWriteFont*>(font.get());
149 dwFont->mFontFace->GetMetrics(aFontMetrics);
150
151 return true;
152 }
153
154 void
ComputeMetrics(AntialiasOption anAAOption)155 gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption)
156 {
157 DWRITE_FONT_METRICS fontMetrics;
158 if (!(mFontEntry->Weight() == 900 &&
159 !mFontEntry->IsUserFont() &&
160 mFontEntry->Name().EqualsLiteral("Arial Black") &&
161 GetFakeMetricsForArialBlack(&fontMetrics)))
162 {
163 mFontFace->GetMetrics(&fontMetrics);
164 }
165
166 if (mStyle.sizeAdjust >= 0.0) {
167 gfxFloat aspect = (gfxFloat)fontMetrics.xHeight /
168 fontMetrics.designUnitsPerEm;
169 mAdjustedSize = mStyle.GetAdjustedSize(aspect);
170 } else {
171 mAdjustedSize = mStyle.size;
172 }
173
174 // Note that GetMeasuringMode depends on mAdjustedSize
175 if ((anAAOption == gfxFont::kAntialiasDefault &&
176 UsingClearType() &&
177 GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) ||
178 anAAOption == gfxFont::kAntialiasSubpixel)
179 {
180 mUseSubpixelPositions = true;
181 // note that this may be reset to FALSE if we determine that a bitmap
182 // strike is going to be used
183 }
184
185 gfxDWriteFontEntry *fe =
186 static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
187 if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) {
188 mAdjustedSize = NS_lround(mAdjustedSize);
189 mUseSubpixelPositions = false;
190 // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA,
191 // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp
192 // which fails to render bitmap glyphs (see bug 626299).
193 // This option will be passed to the cairo_dwrite_scaled_font_t
194 // after creation.
195 mAllowManualShowGlyphs = false;
196 }
197
198 mMetrics = new gfxFont::Metrics;
199 ::memset(mMetrics, 0, sizeof(*mMetrics));
200
201 mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
202
203 mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
204 mMetrics->capHeight = fontMetrics.capHeight * mFUnitsConvFactor;
205
206 mMetrics->maxAscent = ceil(fontMetrics.ascent * mFUnitsConvFactor);
207 mMetrics->maxDescent = ceil(fontMetrics.descent * mFUnitsConvFactor);
208 mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent;
209
210 mMetrics->emHeight = mAdjustedSize;
211 mMetrics->emAscent = mMetrics->emHeight *
212 mMetrics->maxAscent / mMetrics->maxHeight;
213 mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent;
214
215 mMetrics->maxAdvance = mAdjustedSize;
216
217 // try to get the true maxAdvance value from 'hhea'
218 gfxFontEntry::AutoTable hheaTable(GetFontEntry(),
219 TRUETYPE_TAG('h','h','e','a'));
220 if (hheaTable) {
221 uint32_t len;
222 const MetricsHeader* hhea =
223 reinterpret_cast<const MetricsHeader*>
224 (hb_blob_get_data(hheaTable, &len));
225 if (len >= sizeof(MetricsHeader)) {
226 mMetrics->maxAdvance =
227 uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
228 }
229 }
230
231 mMetrics->internalLeading = std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
232 mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
233
234 UINT32 ucs = L' ';
235 UINT16 glyph;
236 HRESULT hr = mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph);
237 if (FAILED(hr)) {
238 mMetrics->spaceWidth = 0;
239 } else {
240 mSpaceGlyph = glyph;
241 mMetrics->spaceWidth = MeasureGlyphWidth(glyph);
242 }
243
244 // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
245 // if the table is not available or if using hinted/pixel-snapped widths
246 if (mUseSubpixelPositions) {
247 mMetrics->aveCharWidth = 0;
248 gfxFontEntry::AutoTable os2Table(GetFontEntry(),
249 TRUETYPE_TAG('O','S','/','2'));
250 if (os2Table) {
251 uint32_t len;
252 const OS2Table* os2 =
253 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
254 if (len >= 4) {
255 // Not checking against sizeof(mozilla::OS2Table) here because older
256 // versions of the table have different sizes; we only need the first
257 // two 16-bit fields here.
258 mMetrics->aveCharWidth =
259 int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor;
260 }
261 }
262 }
263
264 if (mMetrics->aveCharWidth < 1) {
265 ucs = L'x';
266 if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
267 mMetrics->aveCharWidth = MeasureGlyphWidth(glyph);
268 }
269 if (mMetrics->aveCharWidth < 1) {
270 // Let's just assume the X is square.
271 mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
272 }
273 }
274
275 ucs = L'0';
276 if (SUCCEEDED(mFontFace->GetGlyphIndicesW(&ucs, 1, &glyph))) {
277 mMetrics->zeroOrAveCharWidth = MeasureGlyphWidth(glyph);
278 }
279 if (mMetrics->zeroOrAveCharWidth < 1) {
280 mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
281 }
282
283 mMetrics->underlineOffset =
284 fontMetrics.underlinePosition * mFUnitsConvFactor;
285 mMetrics->underlineSize =
286 fontMetrics.underlineThickness * mFUnitsConvFactor;
287 mMetrics->strikeoutOffset =
288 fontMetrics.strikethroughPosition * mFUnitsConvFactor;
289 mMetrics->strikeoutSize =
290 fontMetrics.strikethroughThickness * mFUnitsConvFactor;
291
292 SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
293
294 #if 0
295 printf("Font: %p (%s) size: %f\n", this,
296 NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
297 printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent);
298 printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance);
299 printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading);
300 printf(" spaceWidth: %f aveCharWidth: %f zeroOrAve: %f\n",
301 mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->zeroOrAveCharWidth);
302 printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight);
303 printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n",
304 mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize);
305 #endif
306 }
307
308 using namespace mozilla; // for AutoSwap_* types
309
310 struct EBLCHeader {
311 AutoSwap_PRUint32 version;
312 AutoSwap_PRUint32 numSizes;
313 };
314
315 struct SbitLineMetrics {
316 int8_t ascender;
317 int8_t descender;
318 uint8_t widthMax;
319 int8_t caretSlopeNumerator;
320 int8_t caretSlopeDenominator;
321 int8_t caretOffset;
322 int8_t minOriginSB;
323 int8_t minAdvanceSB;
324 int8_t maxBeforeBL;
325 int8_t minAfterBL;
326 int8_t pad1;
327 int8_t pad2;
328 };
329
330 struct BitmapSizeTable {
331 AutoSwap_PRUint32 indexSubTableArrayOffset;
332 AutoSwap_PRUint32 indexTablesSize;
333 AutoSwap_PRUint32 numberOfIndexSubTables;
334 AutoSwap_PRUint32 colorRef;
335 SbitLineMetrics hori;
336 SbitLineMetrics vert;
337 AutoSwap_PRUint16 startGlyphIndex;
338 AutoSwap_PRUint16 endGlyphIndex;
339 uint8_t ppemX;
340 uint8_t ppemY;
341 uint8_t bitDepth;
342 uint8_t flags;
343 };
344
345 typedef EBLCHeader EBSCHeader;
346
347 struct BitmapScaleTable {
348 SbitLineMetrics hori;
349 SbitLineMetrics vert;
350 uint8_t ppemX;
351 uint8_t ppemY;
352 uint8_t substitutePpemX;
353 uint8_t substitutePpemY;
354 };
355
356 bool
HasBitmapStrikeForSize(uint32_t aSize)357 gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize)
358 {
359 uint8_t *tableData;
360 uint32_t len;
361 void *tableContext;
362 BOOL exists;
363 HRESULT hr =
364 mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'),
365 (const void**)&tableData, &len,
366 &tableContext, &exists);
367 if (FAILED(hr)) {
368 return false;
369 }
370
371 bool hasStrike = false;
372 // not really a loop, but this lets us use 'break' to skip out of the block
373 // as soon as we know the answer, and skips it altogether if the table is
374 // not present
375 while (exists) {
376 if (len < sizeof(EBLCHeader)) {
377 break;
378 }
379 const EBLCHeader *hdr = reinterpret_cast<const EBLCHeader*>(tableData);
380 if (hdr->version != 0x00020000) {
381 break;
382 }
383 uint32_t numSizes = hdr->numSizes;
384 if (numSizes > 0xffff) { // sanity-check, prevent overflow below
385 break;
386 }
387 if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) {
388 break;
389 }
390 const BitmapSizeTable *sizeTable =
391 reinterpret_cast<const BitmapSizeTable*>(hdr + 1);
392 for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) {
393 if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) {
394 // we ignore a strike that contains fewer than 4 glyphs,
395 // as that probably indicates a font such as Courier New
396 // that provides bitmaps ONLY for the "shading" characters
397 // U+2591..2593
398 hasStrike = (uint16_t(sizeTable->endGlyphIndex) >=
399 uint16_t(sizeTable->startGlyphIndex) + 3);
400 break;
401 }
402 }
403 // if we reach here, we didn't find a strike; unconditionally break
404 // out of the while-loop block
405 break;
406 }
407 mFontFace->ReleaseFontTable(tableContext);
408
409 if (hasStrike) {
410 return true;
411 }
412
413 // if we didn't find a real strike, check if the font calls for scaling
414 // another bitmap to this size
415 hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'),
416 (const void**)&tableData, &len,
417 &tableContext, &exists);
418 if (FAILED(hr)) {
419 return false;
420 }
421
422 while (exists) {
423 if (len < sizeof(EBSCHeader)) {
424 break;
425 }
426 const EBSCHeader *hdr = reinterpret_cast<const EBSCHeader*>(tableData);
427 if (hdr->version != 0x00020000) {
428 break;
429 }
430 uint32_t numSizes = hdr->numSizes;
431 if (numSizes > 0xffff) {
432 break;
433 }
434 if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) {
435 break;
436 }
437 const BitmapScaleTable *scaleTable =
438 reinterpret_cast<const BitmapScaleTable*>(hdr + 1);
439 for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) {
440 if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) {
441 hasStrike = true;
442 break;
443 }
444 }
445 break;
446 }
447 mFontFace->ReleaseFontTable(tableContext);
448
449 return hasStrike;
450 }
451
452 uint32_t
GetSpaceGlyph()453 gfxDWriteFont::GetSpaceGlyph()
454 {
455 return mSpaceGlyph;
456 }
457
458 bool
SetupCairoFont(DrawTarget * aDrawTarget)459 gfxDWriteFont::SetupCairoFont(DrawTarget* aDrawTarget)
460 {
461 cairo_scaled_font_t *scaledFont = GetCairoScaledFont();
462 if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) {
463 // Don't cairo_set_scaled_font as that would propagate the error to
464 // the cairo_t, precluding any further drawing.
465 return false;
466 }
467 cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), scaledFont);
468 return true;
469 }
470
471 bool
IsValid() const472 gfxDWriteFont::IsValid() const
473 {
474 return mFontFace != nullptr;
475 }
476
477 IDWriteFontFace*
GetFontFace()478 gfxDWriteFont::GetFontFace()
479 {
480 return mFontFace.get();
481 }
482
483 cairo_font_face_t *
CairoFontFace()484 gfxDWriteFont::CairoFontFace()
485 {
486 if (!mCairoFontFace) {
487 #ifdef CAIRO_HAS_DWRITE_FONT
488 mCairoFontFace =
489 cairo_dwrite_font_face_create_for_dwrite_fontface(
490 ((gfxDWriteFontEntry*)mFontEntry.get())->mFont, mFontFace);
491 #endif
492 }
493 return mCairoFontFace;
494 }
495
496
497 cairo_scaled_font_t *
GetCairoScaledFont()498 gfxDWriteFont::GetCairoScaledFont()
499 {
500 if (!mScaledFont) {
501 cairo_matrix_t sizeMatrix;
502 cairo_matrix_t identityMatrix;
503
504 cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize);
505 cairo_matrix_init_identity(&identityMatrix);
506
507 cairo_font_options_t *fontOptions = cairo_font_options_create();
508 if (mNeedsOblique) {
509 double skewfactor = OBLIQUE_SKEW_FACTOR;
510
511 cairo_matrix_t style;
512 cairo_matrix_init(&style,
513 1, //xx
514 0, //yx
515 -1 * skewfactor, //xy
516 1, //yy
517 0, //x0
518 0); //y0
519 cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style);
520 }
521
522 if (mAntialiasOption != kAntialiasDefault) {
523 cairo_font_options_set_antialias(fontOptions,
524 GetCairoAntialiasOption(mAntialiasOption));
525 }
526
527 mScaledFont = cairo_scaled_font_create(CairoFontFace(),
528 &sizeMatrix,
529 &identityMatrix,
530 fontOptions);
531 cairo_font_options_destroy(fontOptions);
532
533 cairo_dwrite_scaled_font_allow_manual_show_glyphs(mScaledFont,
534 mAllowManualShowGlyphs);
535
536 cairo_dwrite_scaled_font_set_force_GDI_classic(mScaledFont,
537 GetForceGDIClassic());
538 }
539
540 NS_ASSERTION(mAdjustedSize == 0.0 ||
541 cairo_scaled_font_status(mScaledFont)
542 == CAIRO_STATUS_SUCCESS,
543 "Failed to make scaled font");
544
545 return mScaledFont;
546 }
547
548 gfxFont::RunMetrics
Measure(const gfxTextRun * aTextRun,uint32_t aStart,uint32_t aEnd,BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,Spacing * aSpacing,uint16_t aOrientation)549 gfxDWriteFont::Measure(const gfxTextRun* aTextRun,
550 uint32_t aStart, uint32_t aEnd,
551 BoundingBoxType aBoundingBoxType,
552 DrawTarget* aRefDrawTarget,
553 Spacing* aSpacing,
554 uint16_t aOrientation)
555 {
556 gfxFont::RunMetrics metrics =
557 gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType,
558 aRefDrawTarget, aSpacing, aOrientation);
559
560 // if aBoundingBoxType is LOOSE_INK_EXTENTS
561 // and the underlying cairo font may be antialiased,
562 // we can't trust Windows to have considered all the pixels
563 // so we need to add "padding" to the bounds.
564 // (see bugs 475968, 439831, compare also bug 445087)
565 if (aBoundingBoxType == LOOSE_INK_EXTENTS &&
566 mAntialiasOption != kAntialiasNone &&
567 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC &&
568 metrics.mBoundingBox.width > 0) {
569 metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
570 metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3;
571 }
572
573 return metrics;
574 }
575
576 bool
ProvidesGlyphWidths() const577 gfxDWriteFont::ProvidesGlyphWidths() const
578 {
579 return !mUseSubpixelPositions ||
580 (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
581 }
582
583 int32_t
GetGlyphWidth(DrawTarget & aDrawTarget,uint16_t aGID)584 gfxDWriteFont::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
585 {
586 if (!mGlyphWidths) {
587 mGlyphWidths = MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
588 }
589
590 int32_t width = -1;
591 if (mGlyphWidths->Get(aGID, &width)) {
592 return width;
593 }
594
595 width = NS_lround(MeasureGlyphWidth(aGID) * 65536.0);
596 mGlyphWidths->Put(aGID, width);
597 return width;
598 }
599
600 already_AddRefed<GlyphRenderingOptions>
GetGlyphRenderingOptions(const TextRunDrawParams * aRunParams)601 gfxDWriteFont::GetGlyphRenderingOptions(const TextRunDrawParams* aRunParams)
602 {
603 if (UsingClearType()) {
604 return Factory::CreateDWriteGlyphRenderingOptions(
605 gfxWindowsPlatform::GetPlatform()->GetRenderingParams(GetForceGDIClassic() ?
606 gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC : gfxWindowsPlatform::TEXT_RENDERING_NORMAL));
607 } else {
608 return Factory::CreateDWriteGlyphRenderingOptions(gfxWindowsPlatform::GetPlatform()->
609 GetRenderingParams(gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE));
610 }
611 }
612
613 bool
GetForceGDIClassic()614 gfxDWriteFont::GetForceGDIClassic()
615 {
616 return static_cast<gfxDWriteFontEntry*>(mFontEntry.get())->GetForceGDIClassic() &&
617 cairo_dwrite_get_cleartype_rendering_mode() < 0 &&
618 GetAdjustedSize() <=
619 gfxDWriteFontList::PlatformFontList()->GetForceGDIClassicMaxFontSize();
620 }
621
622 DWRITE_MEASURING_MODE
GetMeasuringMode()623 gfxDWriteFont::GetMeasuringMode()
624 {
625 return GetForceGDIClassic()
626 ? DWRITE_MEASURING_MODE_GDI_CLASSIC
627 : gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode();
628 }
629
630 gfxFloat
MeasureGlyphWidth(uint16_t aGlyph)631 gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph)
632 {
633 DWRITE_GLYPH_METRICS metrics;
634 HRESULT hr;
635 if (mUseSubpixelPositions) {
636 hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
637 if (SUCCEEDED(hr)) {
638 return metrics.advanceWidth * mFUnitsConvFactor;
639 }
640 } else {
641 hr = mFontFace->GetGdiCompatibleGlyphMetrics(
642 FLOAT(mAdjustedSize), 1.0f, nullptr,
643 GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL,
644 &aGlyph, 1, &metrics, FALSE);
645 if (SUCCEEDED(hr)) {
646 return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
647 }
648 }
649 return 0;
650 }
651
652 void
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const653 gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
654 FontCacheSizes* aSizes) const
655 {
656 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
657 aSizes->mFontInstances += aMallocSizeOf(mMetrics);
658 if (mGlyphWidths) {
659 aSizes->mFontInstances +=
660 mGlyphWidths->ShallowSizeOfIncludingThis(aMallocSizeOf);
661 }
662 }
663
664 void
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const665 gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
666 FontCacheSizes* aSizes) const
667 {
668 aSizes->mFontInstances += aMallocSizeOf(this);
669 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
670 }
671
672 already_AddRefed<ScaledFont>
GetScaledFont(mozilla::gfx::DrawTarget * aTarget)673 gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget)
674 {
675 bool wantCairo = aTarget->GetBackendType() == BackendType::CAIRO;
676 if (mAzureScaledFont && mAzureScaledFontIsCairo == wantCairo) {
677 RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
678 return scaledFont.forget();
679 }
680
681 NativeFont nativeFont;
682 nativeFont.mType = NativeFontType::DWRITE_FONT_FACE;
683 nativeFont.mFont = GetFontFace();
684
685 if (wantCairo) {
686 mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont,
687 GetAdjustedSize(),
688 GetCairoScaledFont());
689 } else if (aTarget->GetBackendType() == BackendType::SKIA) {
690 gfxDWriteFontEntry *fe =
691 static_cast<gfxDWriteFontEntry*>(mFontEntry.get());
692 bool useEmbeddedBitmap = (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize)));
693
694 const gfxFontStyle* fontStyle = GetStyle();
695 mAzureScaledFont =
696 Factory::CreateScaledFontForDWriteFont(mFontFace, fontStyle,
697 GetAdjustedSize(),
698 useEmbeddedBitmap,
699 GetForceGDIClassic());
700 } else {
701 mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont,
702 GetAdjustedSize());
703 }
704
705 mAzureScaledFontIsCairo = wantCairo;
706
707 RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
708 return scaledFont.forget();
709 }
710