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/. */
7 #include "ScaledFontFontconfig.h"
8 #include "UnscaledFontFreeType.h"
10 #include "mozilla/StaticPrefs_gfx.h"
11 #include "mozilla/webrender/WebRenderTypes.h"
13 #include "skia/include/ports/SkTypeface_cairo.h"
14 #include "HelpersSkia.h"
16 #include <fontconfig/fcfreetype.h>
18 #include FT_LCD_FILTER_H
19 #include FT_MULTIPLE_MASTERS_H
21 namespace mozilla::gfx
{
23 ScaledFontFontconfig::ScaledFontFontconfig(
24 RefPtr
<SharedFTFace
>&& aFace
, FcPattern
* aPattern
,
25 const RefPtr
<UnscaledFont
>& aUnscaledFont
, Float aSize
)
26 : ScaledFontBase(aUnscaledFont
, aSize
),
27 mFace(std::move(aFace
)),
28 mInstanceData(aPattern
) {}
30 ScaledFontFontconfig::ScaledFontFontconfig(
31 RefPtr
<SharedFTFace
>&& aFace
, const InstanceData
& aInstanceData
,
32 const RefPtr
<UnscaledFont
>& aUnscaledFont
, Float aSize
)
33 : ScaledFontBase(aUnscaledFont
, aSize
),
34 mFace(std::move(aFace
)),
35 mInstanceData(aInstanceData
) {}
37 bool ScaledFontFontconfig::UseSubpixelPosition() const {
40 gfx_text_subpixel_position_force_disabled_AtStartup()) &&
41 mInstanceData
.mAntialias
!= AntialiasMode::NONE
&&
42 FT_IS_SCALABLE(mFace
->GetFace()) &&
43 (mInstanceData
.mHinting
== FontHinting::NONE
||
44 mInstanceData
.mHinting
== FontHinting::LIGHT
||
47 gfx_text_subpixel_position_force_enabled_AtStartup()));
50 SkTypeface
* ScaledFontFontconfig::CreateSkTypeface() {
51 SkPixelGeometry geo
= mInstanceData
.mFlags
& InstanceData::SUBPIXEL_BGR
52 ? (mInstanceData
.mFlags
& InstanceData::LCD_VERTICAL
53 ? kBGR_V_SkPixelGeometry
54 : kBGR_H_SkPixelGeometry
)
55 : (mInstanceData
.mFlags
& InstanceData::LCD_VERTICAL
56 ? kRGB_V_SkPixelGeometry
57 : kRGB_H_SkPixelGeometry
);
58 return SkCreateTypefaceFromCairoFTFont(mFace
->GetFace(), mFace
.get(), geo
,
59 mInstanceData
.mLcdFilter
);
62 void ScaledFontFontconfig::SetupSkFontDrawOptions(SkFont
& aFont
) {
63 aFont
.setSubpixel(UseSubpixelPosition());
65 if (mInstanceData
.mFlags
& InstanceData::AUTOHINT
) {
66 aFont
.setForceAutoHinting(true);
68 if (mInstanceData
.mFlags
& InstanceData::EMBEDDED_BITMAP
) {
69 aFont
.setEmbeddedBitmaps(true);
71 if (mInstanceData
.mFlags
& InstanceData::EMBOLDEN
) {
72 aFont
.setEmbolden(true);
75 aFont
.setHinting(GfxHintingToSkiaHinting(mInstanceData
.mHinting
));
78 bool ScaledFontFontconfig::MayUseBitmaps() {
79 return mInstanceData
.mFlags
& InstanceData::EMBEDDED_BITMAP
&&
80 !FT_IS_SCALABLE(mFace
->GetFace());
83 cairo_font_face_t
* ScaledFontFontconfig::CreateCairoFontFace(
84 cairo_font_options_t
* aFontOptions
) {
86 unsigned int synthFlags
;
87 mInstanceData
.SetupFontOptions(aFontOptions
, &loadFlags
, &synthFlags
);
89 return cairo_ft_font_face_create_for_ft_face(mFace
->GetFace(), loadFlags
,
90 synthFlags
, mFace
.get());
93 AntialiasMode
ScaledFontFontconfig::GetDefaultAAMode() {
94 return mInstanceData
.mAntialias
;
97 bool FcPatternAllowsBitmaps(FcPattern
* aPattern
, bool aAntialias
,
100 // Always allow bitmaps when antialiasing is disabled
104 if (FcPatternGetBool(aPattern
, FC_EMBEDDED_BITMAP
, 0, &bitmap
) !=
107 // If bitmaps were explicitly disabled, then disallow them
111 // If hinting is used and bitmaps were enabled, then allow them
114 // When hinting is disabled, then avoid loading bitmaps from outline
115 // fonts. However, emoji fonts may have no outlines while containing
116 // bitmaps intended to be scaled, so still allow those.
118 if (FcPatternGetBool(aPattern
, FC_OUTLINE
, 0, &outline
) == FcResultMatch
&&
123 if (FcPatternGetBool(aPattern
, FC_SCALABLE
, 0, &scalable
) != FcResultMatch
||
130 ScaledFontFontconfig::InstanceData::InstanceData(FcPattern
* aPattern
)
132 mAntialias(AntialiasMode::NONE
),
133 mHinting(FontHinting::NONE
),
134 mLcdFilter(FT_LCD_FILTER_LEGACY
) {
135 // Record relevant Fontconfig properties into instance data.
137 if (FcPatternGetBool(aPattern
, FC_AUTOHINT
, 0, &autohint
) == FcResultMatch
&&
142 if (FcPatternGetBool(aPattern
, FC_EMBOLDEN
, 0, &embolden
) == FcResultMatch
&&
147 // For printer fonts, Cairo hint metrics and hinting will be disabled.
148 // For other fonts, allow hint metrics and hinting.
150 if (FcPatternGetBool(aPattern
, "gfx.printing", 0, &printing
) !=
153 mFlags
|= HINT_METRICS
;
156 if (FcPatternGetBool(aPattern
, FC_HINTING
, 0, &hinting
) != FcResultMatch
||
159 if (FcPatternGetInteger(aPattern
, FC_HINT_STYLE
, 0, &hintstyle
) !=
161 hintstyle
= FC_HINT_FULL
;
165 mHinting
= FontHinting::LIGHT
;
168 mHinting
= FontHinting::NORMAL
;
171 mHinting
= FontHinting::FULL
;
181 if (FcPatternGetBool(aPattern
, FC_ANTIALIAS
, 0, &antialias
) ==
184 // If AA is explicitly disabled, leave bitmaps enabled.
185 mFlags
|= EMBEDDED_BITMAP
;
187 mAntialias
= AntialiasMode::GRAY
;
189 // Otherwise, if AA is enabled, disable embedded bitmaps unless explicitly
191 if (FcPatternAllowsBitmaps(aPattern
, true, mHinting
!= FontHinting::NONE
)) {
192 mFlags
|= EMBEDDED_BITMAP
;
195 // Only record subpixel order and lcd filtering if antialiasing is enabled.
197 if (mFlags
& HINT_METRICS
&&
198 FcPatternGetInteger(aPattern
, FC_RGBA
, 0, &rgba
) == FcResultMatch
) {
204 mAntialias
= AntialiasMode::SUBPIXEL
;
205 if (rgba
== FC_RGBA_VRGB
|| rgba
== FC_RGBA_VBGR
) {
206 mFlags
|= LCD_VERTICAL
;
208 if (rgba
== FC_RGBA_BGR
|| rgba
== FC_RGBA_VBGR
) {
209 mFlags
|= SUBPIXEL_BGR
;
213 case FC_RGBA_UNKNOWN
:
220 if (mAntialias
== AntialiasMode::SUBPIXEL
&&
221 FcPatternGetInteger(aPattern
, FC_LCD_FILTER
, 0, &filter
) ==
225 mLcdFilter
= FT_LCD_FILTER_NONE
;
228 mLcdFilter
= FT_LCD_FILTER_DEFAULT
;
231 mLcdFilter
= FT_LCD_FILTER_LIGHT
;
241 ScaledFontFontconfig::InstanceData::InstanceData(
242 const wr::FontInstanceOptions
* aOptions
,
243 const wr::FontInstancePlatformOptions
* aPlatformOptions
)
244 : mFlags(HINT_METRICS
),
245 mAntialias(AntialiasMode::NONE
),
246 mHinting(FontHinting::FULL
),
247 mLcdFilter(FT_LCD_FILTER_LEGACY
) {
249 if (aOptions
->flags
& wr::FontInstanceFlags::FORCE_AUTOHINT
) {
252 if (aOptions
->flags
& wr::FontInstanceFlags::EMBEDDED_BITMAPS
) {
253 mFlags
|= EMBEDDED_BITMAP
;
255 if (aOptions
->flags
& wr::FontInstanceFlags::SYNTHETIC_BOLD
) {
258 if (aOptions
->render_mode
== wr::FontRenderMode::Subpixel
) {
259 mAntialias
= AntialiasMode::SUBPIXEL
;
260 if (aOptions
->flags
& wr::FontInstanceFlags::SUBPIXEL_BGR
) {
261 mFlags
|= SUBPIXEL_BGR
;
263 if (aOptions
->flags
& wr::FontInstanceFlags::LCD_VERTICAL
) {
264 mFlags
|= LCD_VERTICAL
;
266 } else if (aOptions
->render_mode
!= wr::FontRenderMode::Mono
) {
267 mAntialias
= AntialiasMode::GRAY
;
270 if (aPlatformOptions
) {
271 switch (aPlatformOptions
->hinting
) {
272 case wr::FontHinting::None
:
273 mHinting
= FontHinting::NONE
;
275 case wr::FontHinting::Light
:
276 mHinting
= FontHinting::LIGHT
;
278 case wr::FontHinting::Normal
:
279 mHinting
= FontHinting::NORMAL
;
284 switch (aPlatformOptions
->lcd_filter
) {
285 case wr::FontLCDFilter::None
:
286 mLcdFilter
= FT_LCD_FILTER_NONE
;
288 case wr::FontLCDFilter::Default
:
289 mLcdFilter
= FT_LCD_FILTER_DEFAULT
;
291 case wr::FontLCDFilter::Light
:
292 mLcdFilter
= FT_LCD_FILTER_LIGHT
;
300 void ScaledFontFontconfig::InstanceData::SetupFontOptions(
301 cairo_font_options_t
* aFontOptions
, int* aOutLoadFlags
,
302 unsigned int* aOutSynthFlags
) const {
303 // For regular (non-printer) fonts, enable hint metrics as well as hinting
304 // and (possibly subpixel) antialiasing.
305 cairo_font_options_set_hint_metrics(
307 mFlags
& HINT_METRICS
? CAIRO_HINT_METRICS_ON
: CAIRO_HINT_METRICS_OFF
);
309 cairo_hint_style_t hinting
;
311 case FontHinting::NONE
:
312 hinting
= CAIRO_HINT_STYLE_NONE
;
314 case FontHinting::LIGHT
:
315 hinting
= CAIRO_HINT_STYLE_SLIGHT
;
317 case FontHinting::NORMAL
:
318 hinting
= CAIRO_HINT_STYLE_MEDIUM
;
320 case FontHinting::FULL
:
321 hinting
= CAIRO_HINT_STYLE_FULL
;
324 cairo_font_options_set_hint_style(aFontOptions
, hinting
);
326 switch (mAntialias
) {
327 case AntialiasMode::NONE
:
328 cairo_font_options_set_antialias(aFontOptions
, CAIRO_ANTIALIAS_NONE
);
330 case AntialiasMode::GRAY
:
332 cairo_font_options_set_antialias(aFontOptions
, CAIRO_ANTIALIAS_GRAY
);
334 case AntialiasMode::SUBPIXEL
: {
335 cairo_font_options_set_antialias(aFontOptions
, CAIRO_ANTIALIAS_SUBPIXEL
);
336 cairo_font_options_set_subpixel_order(
338 mFlags
& SUBPIXEL_BGR
339 ? (mFlags
& LCD_VERTICAL
? CAIRO_SUBPIXEL_ORDER_VBGR
340 : CAIRO_SUBPIXEL_ORDER_BGR
)
341 : (mFlags
& LCD_VERTICAL
? CAIRO_SUBPIXEL_ORDER_VRGB
342 : CAIRO_SUBPIXEL_ORDER_RGB
));
343 cairo_lcd_filter_t lcdFilter
= CAIRO_LCD_FILTER_DEFAULT
;
344 switch (mLcdFilter
) {
345 case FT_LCD_FILTER_NONE
:
346 lcdFilter
= CAIRO_LCD_FILTER_NONE
;
348 case FT_LCD_FILTER_DEFAULT
:
349 lcdFilter
= CAIRO_LCD_FILTER_FIR5
;
351 case FT_LCD_FILTER_LIGHT
:
352 lcdFilter
= CAIRO_LCD_FILTER_FIR3
;
354 case FT_LCD_FILTER_LEGACY
:
355 lcdFilter
= CAIRO_LCD_FILTER_INTRA_PIXEL
;
358 cairo_font_options_set_lcd_filter(aFontOptions
, lcdFilter
);
363 // Try to build a sane initial set of Cairo font options based on the
364 // Fontconfig pattern.
365 int loadFlags
= FT_LOAD_DEFAULT
;
366 unsigned int synthFlags
= 0;
368 if (!(mFlags
& EMBEDDED_BITMAP
)) {
369 loadFlags
|= FT_LOAD_NO_BITMAP
;
371 if (mFlags
& AUTOHINT
) {
372 loadFlags
|= FT_LOAD_FORCE_AUTOHINT
;
374 if (mFlags
& EMBOLDEN
) {
375 synthFlags
|= CAIRO_FT_SYNTHESIZE_BOLD
;
378 *aOutLoadFlags
= loadFlags
;
379 *aOutSynthFlags
= synthFlags
;
382 bool ScaledFontFontconfig::GetFontInstanceData(FontInstanceDataOutput aCb
,
384 std::vector
<FontVariation
> variations
;
385 if (HasVariationSettings()) {
386 UnscaledFontFreeType::GetVariationSettingsFromFace(&variations
,
390 aCb(reinterpret_cast<uint8_t*>(&mInstanceData
), sizeof(mInstanceData
),
391 variations
.data(), variations
.size(), aBaton
);
395 bool ScaledFontFontconfig::GetWRFontInstanceOptions(
396 Maybe
<wr::FontInstanceOptions
>* aOutOptions
,
397 Maybe
<wr::FontInstancePlatformOptions
>* aOutPlatformOptions
,
398 std::vector
<FontVariation
>* aOutVariations
) {
399 wr::FontInstanceOptions options
;
400 options
.render_mode
= wr::FontRenderMode::Alpha
;
401 options
.flags
= wr::FontInstanceFlags
{0};
402 if (UseSubpixelPosition()) {
403 options
.flags
|= wr::FontInstanceFlags::SUBPIXEL_POSITION
;
405 options
.synthetic_italics
=
406 wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());
408 wr::FontInstancePlatformOptions platformOptions
;
409 platformOptions
.lcd_filter
= wr::FontLCDFilter::Legacy
;
410 platformOptions
.hinting
= wr::FontHinting::Normal
;
412 if (mInstanceData
.mFlags
& InstanceData::AUTOHINT
) {
413 options
.flags
|= wr::FontInstanceFlags::FORCE_AUTOHINT
;
415 if (mInstanceData
.mFlags
& InstanceData::EMBOLDEN
) {
416 options
.flags
|= wr::FontInstanceFlags::SYNTHETIC_BOLD
;
418 if (mInstanceData
.mFlags
& InstanceData::EMBEDDED_BITMAP
) {
419 options
.flags
|= wr::FontInstanceFlags::EMBEDDED_BITMAPS
;
421 if (mInstanceData
.mAntialias
!= AntialiasMode::NONE
) {
422 if (mInstanceData
.mAntialias
== AntialiasMode::SUBPIXEL
) {
423 options
.render_mode
= wr::FontRenderMode::Subpixel
;
424 platformOptions
.hinting
= wr::FontHinting::LCD
;
425 if (mInstanceData
.mFlags
& InstanceData::LCD_VERTICAL
) {
426 options
.flags
|= wr::FontInstanceFlags::LCD_VERTICAL
;
428 if (mInstanceData
.mFlags
& InstanceData::SUBPIXEL_BGR
) {
429 options
.flags
|= wr::FontInstanceFlags::SUBPIXEL_BGR
;
433 switch (mInstanceData
.mLcdFilter
) {
434 case FT_LCD_FILTER_NONE
:
435 platformOptions
.lcd_filter
= wr::FontLCDFilter::None
;
437 case FT_LCD_FILTER_DEFAULT
:
438 platformOptions
.lcd_filter
= wr::FontLCDFilter::Default
;
440 case FT_LCD_FILTER_LIGHT
:
441 platformOptions
.lcd_filter
= wr::FontLCDFilter::Light
;
443 case FT_LCD_FILTER_LEGACY
:
448 switch (mInstanceData
.mHinting
) {
449 case FontHinting::NONE
:
450 platformOptions
.hinting
= wr::FontHinting::None
;
452 case FontHinting::LIGHT
:
453 platformOptions
.hinting
= wr::FontHinting::Light
;
455 case FontHinting::NORMAL
:
456 platformOptions
.hinting
= wr::FontHinting::Normal
;
458 case FontHinting::FULL
:
462 options
.render_mode
= wr::FontRenderMode::Mono
;
464 switch (mInstanceData
.mHinting
) {
465 case FontHinting::NONE
:
466 platformOptions
.hinting
= wr::FontHinting::None
;
469 platformOptions
.hinting
= wr::FontHinting::Mono
;
474 *aOutOptions
= Some(options
);
475 *aOutPlatformOptions
= Some(platformOptions
);
477 if (HasVariationSettings()) {
478 UnscaledFontFreeType::GetVariationSettingsFromFace(aOutVariations
,
485 already_AddRefed
<ScaledFont
> UnscaledFontFontconfig::CreateScaledFont(
486 Float aSize
, const uint8_t* aInstanceData
, uint32_t aInstanceDataLength
,
487 const FontVariation
* aVariations
, uint32_t aNumVariations
) {
488 if (aInstanceDataLength
< sizeof(ScaledFontFontconfig::InstanceData
)) {
489 gfxWarning() << "Fontconfig scaled font instance data is truncated.";
492 const ScaledFontFontconfig::InstanceData
& instanceData
=
493 *reinterpret_cast<const ScaledFontFontconfig::InstanceData
*>(
496 RefPtr
<SharedFTFace
> face(InitFace());
498 gfxWarning() << "Attempted to deserialize Fontconfig scaled font without "
503 if (aNumVariations
> 0 && face
->GetData()) {
504 if (RefPtr
<SharedFTFace
> varFace
= face
->GetData()->CloneFace()) {
509 // Only apply variations if we have an explicitly cloned face.
510 if (aNumVariations
> 0 && face
!= GetFace()) {
511 ApplyVariationsToFace(aVariations
, aNumVariations
, face
->GetFace());
514 RefPtr
<ScaledFontFontconfig
> scaledFont
=
515 new ScaledFontFontconfig(std::move(face
), instanceData
, this, aSize
);
517 return scaledFont
.forget();
520 already_AddRefed
<ScaledFont
> UnscaledFontFontconfig::CreateScaledFontFromWRFont(
521 Float aGlyphSize
, const wr::FontInstanceOptions
* aOptions
,
522 const wr::FontInstancePlatformOptions
* aPlatformOptions
,
523 const FontVariation
* aVariations
, uint32_t aNumVariations
) {
524 ScaledFontFontconfig::InstanceData
instanceData(aOptions
, aPlatformOptions
);
525 return CreateScaledFont(aGlyphSize
, reinterpret_cast<uint8_t*>(&instanceData
),
526 sizeof(instanceData
), aVariations
, aNumVariations
);
529 bool ScaledFontFontconfig::HasVariationSettings() {
530 // Check if the FT face has been cloned.
532 mFace
->GetFace()->face_flags
& FT_FACE_FLAG_MULTIPLE_MASTERS
&&
533 mFace
!= static_cast<UnscaledFontFontconfig
*>(mUnscaledFont
.get())
537 already_AddRefed
<UnscaledFont
> UnscaledFontFontconfig::CreateFromFontDescriptor(
538 const uint8_t* aData
, uint32_t aDataLength
, uint32_t aIndex
) {
539 if (aDataLength
== 0) {
540 gfxWarning() << "Fontconfig font descriptor is truncated.";
543 const char* path
= reinterpret_cast<const char*>(aData
);
544 RefPtr
<UnscaledFont
> unscaledFont
=
545 new UnscaledFontFontconfig(std::string(path
, aDataLength
), aIndex
);
546 return unscaledFont
.forget();
549 } // namespace mozilla::gfx