зеркало из https://github.com/mozilla/gecko-dev.git
224 строки
7.1 KiB
C++
224 строки
7.1 KiB
C++
/* -*- 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.flags |= wr::FontInstanceFlags_EMBEDDED_BITMAPS;
|
|
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;
|
|
if (face->face_flags & FT_FACE_FLAG_TRICKY) {
|
|
flags &= ~FT_LOAD_NO_AUTOHINT;
|
|
}
|
|
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
|