tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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