DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (c3d72d38c09e)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ScaledFontFreeType.h"
#include "UnscaledFontFreeType.h"
#include "NativeFontResourceFreeType.h"
#include "Logging.h"
#include "StackArray.h"
#include "mozilla/webrender/WebRenderTypes.h"

#ifdef USE_SKIA
#  include "skia/include/ports/SkTypeface_cairo.h"
#endif

#include FT_MULTIPLE_MASTERS_H

namespace mozilla {
namespace gfx {

// On Linux and Android our "platform" font is a cairo_scaled_font_t and we use
// an SkFontHost implementation that allows Skia to render using this.
// This is mainly because FT_Face is not good for sharing between libraries,
// which is a requirement when we consider runtime switchable backends and so on
ScaledFontFreeType::ScaledFontFreeType(
    cairo_scaled_font_t* aScaledFont, FT_Face aFace,
    const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize)
    : ScaledFontBase(aUnscaledFont, aSize), mFace(aFace) {
  SetCairoScaledFont(aScaledFont);
}

#ifdef USE_SKIA
SkTypeface* ScaledFontFreeType::CreateSkTypeface() {
  return SkCreateTypefaceFromCairoFTFont(mScaledFont, mFace);
}
#endif

bool ScaledFontFreeType::GetFontInstanceData(FontInstanceDataOutput aCb,
                                             void* aBaton) {
  std::vector<FontVariation> variations;
  if (HasVariationSettings()) {
    UnscaledFontFreeType::GetVariationSettingsFromFace(&variations, mFace);
  }

  aCb(nullptr, 0, variations.data(), variations.size(), aBaton);
  return true;
}

bool ScaledFontFreeType::GetWRFontInstanceOptions(
    Maybe<wr::FontInstanceOptions>* aOutOptions,
    Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
    std::vector<FontVariation>* aOutVariations) {
  wr::FontInstanceOptions options;
  options.render_mode = wr::FontRenderMode::Alpha;
  // FIXME: Cairo-FT metrics are not compatible with subpixel positioning.
  // options.flags = wr::FontInstanceFlags_SUBPIXEL_POSITION;
  options.flags = wr::FontInstanceFlags{0};
  options.bg_color = wr::ToColorU(Color());
  options.synthetic_italics =
      wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());

  wr::FontInstancePlatformOptions platformOptions;
  platformOptions.lcd_filter = wr::FontLCDFilter::None;
  platformOptions.hinting = wr::FontHinting::None;

  *aOutOptions = Some(options);
  *aOutPlatformOptions = Some(platformOptions);

  if (HasVariationSettings()) {
    UnscaledFontFreeType::GetVariationSettingsFromFace(aOutVariations, mFace);
  }

  return true;
}

FT_Face UnscaledFontFreeType::InitFace() {
  if (mFace) {
    return mFace;
  }
  if (mFile.empty()) {
    return nullptr;
  }
  FT_Face face = Factory::NewFTFace(nullptr, mFile.c_str(), mIndex);
  if (!face) {
    gfxWarning() << "Failed initializing FreeType face from filename";
    return nullptr;
  }
  mOwnsFace = true;
  mFace = face;
  return mFace;
}

static cairo_user_data_key_t sNativeFontResourceKey;

static void ReleaseNativeFontResource(void* aData) {
  static_cast<NativeFontResource*>(aData)->Release();
}

static cairo_user_data_key_t sFaceKey;

static void ReleaseFace(void* aData) {
  Factory::ReleaseFTFace(static_cast<FT_Face>(aData));
}

already_AddRefed<ScaledFont> UnscaledFontFreeType::CreateScaledFont(
    Float aGlyphSize, const uint8_t* aInstanceData,
    uint32_t aInstanceDataLength, const FontVariation* aVariations,
    uint32_t aNumVariations) {
  FT_Face face = InitFace();
  if (!face) {
    gfxWarning() << "Attempted to deserialize FreeType scaled font without "
                    "FreeType face";
    return nullptr;
  }

  NativeFontResourceFreeType* nfr =
      static_cast<NativeFontResourceFreeType*>(mNativeFontResource.get());
  FT_Face varFace = nullptr;
  if (nfr && aNumVariations > 0) {
    varFace = nfr->CloneFace();
    if (varFace) {
      face = varFace;
    } else {
      gfxWarning() << "Failed cloning face for variations";
    }
  }

  StackArray<FT_Fixed, 32> coords(aNumVariations);
  for (uint32_t i = 0; i < aNumVariations; i++) {
    coords[i] = std::round(aVariations[i].mValue * 65536.0);
  }

  int flags = FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
  cairo_font_face_t* font = cairo_ft_font_face_create_for_ft_face(
      face, flags, coords.data(), aNumVariations);
  if (cairo_font_face_status(font) != CAIRO_STATUS_SUCCESS) {
    gfxWarning() << "Failed creating Cairo font face for FreeType face";
    if (varFace) {
      Factory::ReleaseFTFace(varFace);
    }
    return nullptr;
  }

  if (nfr) {
    // Bug 1362117 - Cairo may keep the font face alive after the owning
    // NativeFontResource was freed. To prevent this, we must bind the
    // NativeFontResource to the font face so that it stays alive at least as
    // long as the font face.
    nfr->AddRef();
    cairo_status_t err = CAIRO_STATUS_SUCCESS;
    bool cleanupFace = false;
    if (varFace) {
      err =
          cairo_font_face_set_user_data(font, &sFaceKey, varFace, ReleaseFace);
    }

    if (err != CAIRO_STATUS_SUCCESS) {
      cleanupFace = true;
    } else {
      err = cairo_font_face_set_user_data(font, &sNativeFontResourceKey, nfr,
                                          ReleaseNativeFontResource);
    }
    if (err != CAIRO_STATUS_SUCCESS) {
      gfxWarning() << "Failed binding NativeFontResource to Cairo font face";
      if (varFace && cleanupFace) {
        Factory::ReleaseFTFace(varFace);
      }
      nfr->Release();
      cairo_font_face_destroy(font);
      return nullptr;
    }
  }

  cairo_matrix_t sizeMatrix;
  cairo_matrix_init(&sizeMatrix, aGlyphSize, 0, 0, aGlyphSize, 0, 0);

  cairo_matrix_t identityMatrix;
  cairo_matrix_init_identity(&identityMatrix);

  cairo_font_options_t* fontOptions = cairo_font_options_create();
  cairo_font_options_set_hint_metrics(fontOptions, CAIRO_HINT_METRICS_OFF);

  cairo_scaled_font_t* cairoScaledFont =
      cairo_scaled_font_create(font, &sizeMatrix, &identityMatrix, fontOptions);

  cairo_font_options_destroy(fontOptions);

  cairo_font_face_destroy(font);

  if (cairo_scaled_font_status(cairoScaledFont) != CAIRO_STATUS_SUCCESS) {
    gfxWarning() << "Failed creating Cairo scaled font for font face";
    return nullptr;
  }

  RefPtr<ScaledFontFreeType> scaledFont =
      new ScaledFontFreeType(cairoScaledFont, face, this, aGlyphSize);

  cairo_scaled_font_destroy(cairoScaledFont);

  // Only apply variations if we have an explicitly cloned face. Otherwise,
  // if the pattern holds the pathname, Cairo will handle setting of variations.
  if (varFace) {
    ApplyVariationsToFace(aVariations, aNumVariations, varFace);
  }

  return scaledFont.forget();
}

bool ScaledFontFreeType::HasVariationSettings() {
  // Check if the FT face has been cloned.
  return mFace && mFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS &&
         mFace !=
             static_cast<UnscaledFontFreeType*>(mUnscaledFont.get())->GetFace();
}

}  // namespace gfx
}  // namespace mozilla