/* -*- 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& aUnscaledFont, Float aSize) : ScaledFontBase(aUnscaledFont, aSize) , mFace(aFace) { SetCairoScaledFont(aScaledFont); } #ifdef USE_SKIA SkTypeface* ScaledFontFreeType::CreateSkTypeface() { return SkCreateTypefaceFromCairoFTFont(mScaledFont); } #endif bool ScaledFontFreeType::GetFontInstanceData(FontInstanceDataOutput aCb, void* aBaton) { std::vector variations; if (HasVariationSettings()) { UnscaledFontFreeType::GetVariationSettingsFromFace(&variations, mFace); } aCb(nullptr, 0, variations.data(), variations.size(), aBaton); return true; } bool ScaledFontFreeType::GetWRFontInstanceOptions(Maybe* aOutOptions, Maybe* aOutPlatformOptions, std::vector* 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 = 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; } static cairo_user_data_key_t sNativeFontResourceKey; static void ReleaseNativeFontResource(void* aData) { static_cast(aData)->Release(); } static cairo_user_data_key_t sFaceKey; static void ReleaseFace(void* aData) { Factory::ReleaseFTFace(static_cast(aData)); } already_AddRefed UnscaledFontFreeType::CreateScaledFont(Float aGlyphSize, const uint8_t* aInstanceData, uint32_t aInstanceDataLength, const FontVariation* aVariations, uint32_t aNumVariations) { FT_Face face = GetFace(); if (!face) { gfxWarning() << "Attempted to deserialize FreeType scaled font without FreeType face"; return nullptr; } NativeFontResourceFreeType* nfr = static_cast(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 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 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(mUnscaledFont.get())->GetFace(); } } // namespace gfx } // namespace mozilla