ScaledFontFontconfig.cpp (17854B)
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 "ScaledFontFontconfig.h" 8 #include "UnscaledFontFreeType.h" 9 #include "Logging.h" 10 #include "mozilla/StaticPrefs_gfx.h" 11 #include "mozilla/webrender/WebRenderTypes.h" 12 13 #include "skia/include/ports/SkTypeface_cairo.h" 14 #include "HelpersSkia.h" 15 16 #include <fontconfig/fcfreetype.h> 17 18 #include FT_LCD_FILTER_H 19 #include FT_MULTIPLE_MASTERS_H 20 21 namespace mozilla::gfx { 22 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) {} 29 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) {} 36 37 bool ScaledFontFontconfig::UseSubpixelPosition() const { 38 return !MOZ_UNLIKELY( 39 StaticPrefs:: 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 || 45 MOZ_UNLIKELY( 46 StaticPrefs:: 47 gfx_text_subpixel_position_force_enabled_AtStartup())); 48 } 49 50 SkTypeface* ScaledFontFontconfig::CreateSkTypeface() { 51 return SkCreateTypefaceFromCairoFTFont(mFace->GetFace(), mFace.get(), 52 mInstanceData.mLcdFilter); 53 } 54 55 void ScaledFontFontconfig::SetupSkFontDrawOptions(SkFont& aFont) { 56 aFont.setSubpixel(UseSubpixelPosition()); 57 58 if (mInstanceData.mFlags & InstanceData::AUTOHINT) { 59 aFont.setForceAutoHinting(true); 60 } 61 if (mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP) { 62 aFont.setEmbeddedBitmaps(true); 63 } 64 if (mInstanceData.mFlags & InstanceData::EMBOLDEN) { 65 aFont.setEmbolden(true); 66 } 67 68 aFont.setHinting(GfxHintingToSkiaHinting(mInstanceData.mHinting)); 69 } 70 71 bool ScaledFontFontconfig::MayUseBitmaps() { 72 return mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP && 73 !FT_IS_SCALABLE(mFace->GetFace()); 74 } 75 76 cairo_font_face_t* ScaledFontFontconfig::CreateCairoFontFace( 77 cairo_font_options_t* aFontOptions) { 78 int loadFlags; 79 unsigned int synthFlags; 80 mInstanceData.SetupFontOptions(aFontOptions, &loadFlags, &synthFlags); 81 82 return cairo_ft_font_face_create_for_ft_face(mFace->GetFace(), loadFlags, 83 synthFlags, mFace.get()); 84 } 85 86 AntialiasMode ScaledFontFontconfig::GetDefaultAAMode() { 87 return mInstanceData.mAntialias; 88 } 89 90 bool FcPatternAllowsBitmaps(FcPattern* aPattern, bool aAntialias, 91 bool aHinting) { 92 if (!aAntialias) { 93 // Always allow bitmaps when antialiasing is disabled 94 return true; 95 } 96 FcBool bitmap; 97 if (FcPatternGetBool(aPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) != 98 FcResultMatch || 99 !bitmap) { 100 // If bitmaps were explicitly disabled, then disallow them 101 return false; 102 } 103 if (aHinting) { 104 // If hinting is used and bitmaps were enabled, then allow them 105 return true; 106 } 107 // When hinting is disabled, then avoid loading bitmaps from outline 108 // fonts. However, emoji fonts may have no outlines while containing 109 // bitmaps intended to be scaled, so still allow those. 110 FcBool outline; 111 if (FcPatternGetBool(aPattern, FC_OUTLINE, 0, &outline) == FcResultMatch && 112 outline) { 113 return false; 114 } 115 FcBool scalable; 116 if (FcPatternGetBool(aPattern, FC_SCALABLE, 0, &scalable) != FcResultMatch || 117 !scalable) { 118 return false; 119 } 120 return true; 121 } 122 123 ScaledFontFontconfig::InstanceData::InstanceData(FcPattern* aPattern) 124 : mFlags(0), 125 mAntialias(AntialiasMode::NONE), 126 mHinting(FontHinting::NONE), 127 mLcdFilter(FT_LCD_FILTER_LEGACY) { 128 // Record relevant Fontconfig properties into instance data. 129 FcBool autohint; 130 if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch && 131 autohint) { 132 mFlags |= AUTOHINT; 133 } 134 FcBool embolden; 135 if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch && 136 embolden) { 137 mFlags |= EMBOLDEN; 138 } 139 140 // For printer fonts, Cairo hint metrics and hinting will be disabled. 141 // For other fonts, allow hint metrics and hinting. 142 FcBool printing; 143 if (FcPatternGetBool(aPattern, "gfx.printing", 0, &printing) != 144 FcResultMatch || 145 !printing) { 146 mFlags |= HINT_METRICS; 147 148 FcBool hinting; 149 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch || 150 hinting) { 151 int hintstyle; 152 if (FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &hintstyle) != 153 FcResultMatch) { 154 hintstyle = FC_HINT_FULL; 155 } 156 switch (hintstyle) { 157 case FC_HINT_SLIGHT: 158 mHinting = FontHinting::LIGHT; 159 break; 160 case FC_HINT_MEDIUM: 161 mHinting = FontHinting::NORMAL; 162 break; 163 case FC_HINT_FULL: 164 mHinting = FontHinting::FULL; 165 break; 166 case FC_HINT_NONE: 167 default: 168 break; 169 } 170 } 171 } 172 173 FcBool antialias; 174 if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &antialias) == 175 FcResultMatch && 176 !antialias) { 177 // If AA is explicitly disabled, leave bitmaps enabled. 178 mFlags |= EMBEDDED_BITMAP; 179 } else { 180 mAntialias = AntialiasMode::GRAY; 181 182 // Otherwise, if AA is enabled, disable embedded bitmaps unless explicitly 183 // enabled. 184 if (FcPatternAllowsBitmaps(aPattern, true, mHinting != FontHinting::NONE)) { 185 mFlags |= EMBEDDED_BITMAP; 186 } 187 188 // Only record subpixel order and lcd filtering if antialiasing is enabled. 189 int rgba; 190 if (mFlags & HINT_METRICS && 191 FcPatternGetInteger(aPattern, FC_RGBA, 0, &rgba) == FcResultMatch) { 192 switch (rgba) { 193 case FC_RGBA_RGB: 194 case FC_RGBA_BGR: 195 case FC_RGBA_VRGB: 196 case FC_RGBA_VBGR: 197 mAntialias = AntialiasMode::SUBPIXEL; 198 if (rgba == FC_RGBA_VRGB || rgba == FC_RGBA_VBGR) { 199 mFlags |= LCD_VERTICAL; 200 } 201 if (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR) { 202 mFlags |= SUBPIXEL_BGR; 203 } 204 break; 205 case FC_RGBA_NONE: 206 case FC_RGBA_UNKNOWN: 207 default: 208 break; 209 } 210 } 211 212 int filter; 213 if (mAntialias == AntialiasMode::SUBPIXEL && 214 FcPatternGetInteger(aPattern, FC_LCD_FILTER, 0, &filter) == 215 FcResultMatch) { 216 switch (filter) { 217 case FC_LCD_NONE: 218 mLcdFilter = FT_LCD_FILTER_NONE; 219 break; 220 case FC_LCD_DEFAULT: 221 mLcdFilter = FT_LCD_FILTER_DEFAULT; 222 break; 223 case FC_LCD_LIGHT: 224 mLcdFilter = FT_LCD_FILTER_LIGHT; 225 break; 226 case FC_LCD_LEGACY: 227 default: 228 break; 229 } 230 } 231 } 232 } 233 234 ScaledFontFontconfig::InstanceData::InstanceData( 235 const wr::FontInstanceOptions* aOptions, 236 const wr::FontInstancePlatformOptions* aPlatformOptions) 237 : mFlags(HINT_METRICS), 238 mAntialias(AntialiasMode::NONE), 239 mHinting(FontHinting::FULL), 240 mLcdFilter(FT_LCD_FILTER_LEGACY) { 241 if (aOptions) { 242 if (aOptions->flags & wr::FontInstanceFlags::FORCE_AUTOHINT) { 243 mFlags |= AUTOHINT; 244 } 245 if (aOptions->flags & wr::FontInstanceFlags::EMBEDDED_BITMAPS) { 246 mFlags |= EMBEDDED_BITMAP; 247 } 248 if (aOptions->flags & wr::FontInstanceFlags::SYNTHETIC_BOLD) { 249 mFlags |= EMBOLDEN; 250 } 251 if (aOptions->render_mode == wr::FontRenderMode::Subpixel) { 252 mAntialias = AntialiasMode::SUBPIXEL; 253 if (aOptions->flags & wr::FontInstanceFlags::SUBPIXEL_BGR) { 254 mFlags |= SUBPIXEL_BGR; 255 } 256 if (aOptions->flags & wr::FontInstanceFlags::LCD_VERTICAL) { 257 mFlags |= LCD_VERTICAL; 258 } 259 } else if (aOptions->render_mode != wr::FontRenderMode::Mono) { 260 mAntialias = AntialiasMode::GRAY; 261 } 262 } 263 if (aPlatformOptions) { 264 switch (aPlatformOptions->hinting) { 265 case wr::FontHinting::None: 266 mHinting = FontHinting::NONE; 267 break; 268 case wr::FontHinting::Light: 269 mHinting = FontHinting::LIGHT; 270 break; 271 case wr::FontHinting::Normal: 272 mHinting = FontHinting::NORMAL; 273 break; 274 default: 275 break; 276 } 277 switch (aPlatformOptions->lcd_filter) { 278 case wr::FontLCDFilter::None: 279 mLcdFilter = FT_LCD_FILTER_NONE; 280 break; 281 case wr::FontLCDFilter::Default: 282 mLcdFilter = FT_LCD_FILTER_DEFAULT; 283 break; 284 case wr::FontLCDFilter::Light: 285 mLcdFilter = FT_LCD_FILTER_LIGHT; 286 break; 287 default: 288 break; 289 } 290 } 291 } 292 293 void ScaledFontFontconfig::InstanceData::SetupFontOptions( 294 cairo_font_options_t* aFontOptions, int* aOutLoadFlags, 295 unsigned int* aOutSynthFlags) const { 296 // For regular (non-printer) fonts, enable hint metrics as well as hinting 297 // and (possibly subpixel) antialiasing. 298 cairo_font_options_set_hint_metrics( 299 aFontOptions, 300 mFlags & HINT_METRICS ? CAIRO_HINT_METRICS_ON : CAIRO_HINT_METRICS_OFF); 301 302 cairo_hint_style_t hinting; 303 switch (mHinting) { 304 case FontHinting::NONE: 305 hinting = CAIRO_HINT_STYLE_NONE; 306 break; 307 case FontHinting::LIGHT: 308 hinting = CAIRO_HINT_STYLE_SLIGHT; 309 break; 310 case FontHinting::NORMAL: 311 hinting = CAIRO_HINT_STYLE_MEDIUM; 312 break; 313 case FontHinting::FULL: 314 hinting = CAIRO_HINT_STYLE_FULL; 315 break; 316 } 317 cairo_font_options_set_hint_style(aFontOptions, hinting); 318 319 switch (mAntialias) { 320 case AntialiasMode::NONE: 321 cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_NONE); 322 break; 323 case AntialiasMode::GRAY: 324 default: 325 cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_GRAY); 326 break; 327 case AntialiasMode::SUBPIXEL: { 328 cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_SUBPIXEL); 329 cairo_font_options_set_subpixel_order( 330 aFontOptions, 331 mFlags & SUBPIXEL_BGR 332 ? (mFlags & LCD_VERTICAL ? CAIRO_SUBPIXEL_ORDER_VBGR 333 : CAIRO_SUBPIXEL_ORDER_BGR) 334 : (mFlags & LCD_VERTICAL ? CAIRO_SUBPIXEL_ORDER_VRGB 335 : CAIRO_SUBPIXEL_ORDER_RGB)); 336 cairo_lcd_filter_t lcdFilter = CAIRO_LCD_FILTER_DEFAULT; 337 switch (mLcdFilter) { 338 case FT_LCD_FILTER_NONE: 339 lcdFilter = CAIRO_LCD_FILTER_NONE; 340 break; 341 case FT_LCD_FILTER_DEFAULT: 342 lcdFilter = CAIRO_LCD_FILTER_FIR5; 343 break; 344 case FT_LCD_FILTER_LIGHT: 345 lcdFilter = CAIRO_LCD_FILTER_FIR3; 346 break; 347 case FT_LCD_FILTER_LEGACY: 348 lcdFilter = CAIRO_LCD_FILTER_INTRA_PIXEL; 349 break; 350 } 351 cairo_font_options_set_lcd_filter(aFontOptions, lcdFilter); 352 break; 353 } 354 } 355 356 // Try to build a sane initial set of Cairo font options based on the 357 // Fontconfig pattern. 358 int loadFlags = FT_LOAD_DEFAULT; 359 unsigned int synthFlags = 0; 360 361 if (!(mFlags & EMBEDDED_BITMAP)) { 362 loadFlags |= FT_LOAD_NO_BITMAP; 363 } 364 if (mFlags & AUTOHINT) { 365 loadFlags |= FT_LOAD_FORCE_AUTOHINT; 366 } 367 if (mFlags & EMBOLDEN) { 368 synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD; 369 } 370 371 *aOutLoadFlags = loadFlags; 372 *aOutSynthFlags = synthFlags; 373 } 374 375 bool ScaledFontFontconfig::GetFontInstanceData(FontInstanceDataOutput aCb, 376 void* aBaton) { 377 std::vector<FontVariation> variations; 378 if (HasVariationSettings()) { 379 UnscaledFontFreeType::GetVariationSettingsFromFace(&variations, 380 mFace->GetFace()); 381 } 382 383 aCb(reinterpret_cast<uint8_t*>(&mInstanceData), sizeof(mInstanceData), 384 variations.data(), variations.size(), aBaton); 385 return true; 386 } 387 388 bool ScaledFontFontconfig::GetWRFontInstanceOptions( 389 Maybe<wr::FontInstanceOptions>* aOutOptions, 390 Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions, 391 std::vector<FontVariation>* aOutVariations) { 392 wr::FontInstanceOptions options = {}; 393 options.render_mode = wr::FontRenderMode::Alpha; 394 options.flags = wr::FontInstanceFlags{0}; 395 if (UseSubpixelPosition()) { 396 options.flags |= wr::FontInstanceFlags::SUBPIXEL_POSITION; 397 } 398 options.synthetic_italics = 399 wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle()); 400 401 wr::FontInstancePlatformOptions platformOptions; 402 platformOptions.lcd_filter = wr::FontLCDFilter::Legacy; 403 platformOptions.hinting = wr::FontHinting::Normal; 404 405 if (mInstanceData.mFlags & InstanceData::AUTOHINT) { 406 options.flags |= wr::FontInstanceFlags::FORCE_AUTOHINT; 407 } 408 if (mInstanceData.mFlags & InstanceData::EMBOLDEN) { 409 options.flags |= wr::FontInstanceFlags::SYNTHETIC_BOLD; 410 } 411 if (mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP) { 412 options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS; 413 } 414 if (mInstanceData.mAntialias != AntialiasMode::NONE) { 415 if (mInstanceData.mAntialias == AntialiasMode::SUBPIXEL) { 416 options.render_mode = wr::FontRenderMode::Subpixel; 417 platformOptions.hinting = wr::FontHinting::LCD; 418 if (mInstanceData.mFlags & InstanceData::LCD_VERTICAL) { 419 options.flags |= wr::FontInstanceFlags::LCD_VERTICAL; 420 } 421 if (mInstanceData.mFlags & InstanceData::SUBPIXEL_BGR) { 422 options.flags |= wr::FontInstanceFlags::SUBPIXEL_BGR; 423 } 424 } 425 426 switch (mInstanceData.mLcdFilter) { 427 case FT_LCD_FILTER_NONE: 428 platformOptions.lcd_filter = wr::FontLCDFilter::None; 429 break; 430 case FT_LCD_FILTER_DEFAULT: 431 platformOptions.lcd_filter = wr::FontLCDFilter::Default; 432 break; 433 case FT_LCD_FILTER_LIGHT: 434 platformOptions.lcd_filter = wr::FontLCDFilter::Light; 435 break; 436 case FT_LCD_FILTER_LEGACY: 437 default: 438 break; 439 } 440 441 switch (mInstanceData.mHinting) { 442 case FontHinting::NONE: 443 platformOptions.hinting = wr::FontHinting::None; 444 break; 445 case FontHinting::LIGHT: 446 platformOptions.hinting = wr::FontHinting::Light; 447 break; 448 case FontHinting::NORMAL: 449 platformOptions.hinting = wr::FontHinting::Normal; 450 break; 451 case FontHinting::FULL: 452 break; 453 } 454 } else { 455 options.render_mode = wr::FontRenderMode::Mono; 456 457 switch (mInstanceData.mHinting) { 458 case FontHinting::NONE: 459 platformOptions.hinting = wr::FontHinting::None; 460 break; 461 default: 462 platformOptions.hinting = wr::FontHinting::Mono; 463 break; 464 } 465 } 466 467 *aOutOptions = Some(options); 468 *aOutPlatformOptions = Some(platformOptions); 469 470 if (HasVariationSettings()) { 471 UnscaledFontFreeType::GetVariationSettingsFromFace(aOutVariations, 472 mFace->GetFace()); 473 } 474 475 return true; 476 } 477 478 already_AddRefed<ScaledFont> UnscaledFontFontconfig::CreateScaledFont( 479 Float aSize, const uint8_t* aInstanceData, uint32_t aInstanceDataLength, 480 const FontVariation* aVariations, uint32_t aNumVariations) { 481 if (aInstanceDataLength < sizeof(ScaledFontFontconfig::InstanceData)) { 482 gfxWarning() << "Fontconfig scaled font instance data is truncated."; 483 return nullptr; 484 } 485 const ScaledFontFontconfig::InstanceData& instanceData = 486 *reinterpret_cast<const ScaledFontFontconfig::InstanceData*>( 487 aInstanceData); 488 489 RefPtr<SharedFTFace> face(InitFace()); 490 if (!face) { 491 gfxWarning() << "Attempted to deserialize Fontconfig scaled font without " 492 "FreeType face"; 493 return nullptr; 494 } 495 496 if (aNumVariations > 0 && face->GetData()) { 497 if (RefPtr<SharedFTFace> varFace = face->GetData()->CloneFace()) { 498 face = varFace; 499 } 500 } 501 502 // Only apply variations if we have an explicitly cloned face. 503 if (aNumVariations > 0 && face != GetFace()) { 504 ApplyVariationsToFace(aVariations, aNumVariations, face->GetFace()); 505 } 506 507 RefPtr<ScaledFontFontconfig> scaledFont = 508 new ScaledFontFontconfig(std::move(face), instanceData, this, aSize); 509 510 return scaledFont.forget(); 511 } 512 513 already_AddRefed<ScaledFont> UnscaledFontFontconfig::CreateScaledFontFromWRFont( 514 Float aGlyphSize, const wr::FontInstanceOptions* aOptions, 515 const wr::FontInstancePlatformOptions* aPlatformOptions, 516 const FontVariation* aVariations, uint32_t aNumVariations) { 517 ScaledFontFontconfig::InstanceData instanceData(aOptions, aPlatformOptions); 518 return CreateScaledFont(aGlyphSize, reinterpret_cast<uint8_t*>(&instanceData), 519 sizeof(instanceData), aVariations, aNumVariations); 520 } 521 522 bool ScaledFontFontconfig::HasVariationSettings() { 523 // Check if the FT face has been cloned. 524 return mFace && 525 mFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS && 526 mFace != static_cast<UnscaledFontFontconfig*>(mUnscaledFont.get()) 527 ->GetFace(); 528 } 529 530 already_AddRefed<UnscaledFont> UnscaledFontFontconfig::CreateFromFontDescriptor( 531 const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) { 532 if (aDataLength == 0) { 533 gfxWarning() << "Fontconfig font descriptor is truncated."; 534 return nullptr; 535 } 536 const char* path = reinterpret_cast<const char*>(aData); 537 RefPtr<UnscaledFont> unscaledFont = 538 new UnscaledFontFontconfig(std::string(path, aDataLength), aIndex); 539 return unscaledFont.forget(); 540 } 541 542 } // namespace mozilla::gfx