summaryrefslogtreecommitdiffstats
path: root/gfx/2d/ScaledFontFontconfig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/2d/ScaledFontFontconfig.cpp')
-rw-r--r--gfx/2d/ScaledFontFontconfig.cpp550
1 files changed, 550 insertions, 0 deletions
diff --git a/gfx/2d/ScaledFontFontconfig.cpp b/gfx/2d/ScaledFontFontconfig.cpp
new file mode 100644
index 0000000000..e3025c1072
--- /dev/null
+++ b/gfx/2d/ScaledFontFontconfig.cpp
@@ -0,0 +1,550 @@
+/* -*- 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 "ScaledFontFontconfig.h"
+#include "UnscaledFontFreeType.h"
+#include "Logging.h"
+#include "mozilla/StaticPrefs_gfx.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+#include "skia/include/ports/SkTypeface_cairo.h"
+#include "HelpersSkia.h"
+
+#include <fontconfig/fcfreetype.h>
+
+#include FT_LCD_FILTER_H
+#include FT_MULTIPLE_MASTERS_H
+
+namespace mozilla::gfx {
+
+ScaledFontFontconfig::ScaledFontFontconfig(
+ RefPtr<SharedFTFace>&& aFace, FcPattern* aPattern,
+ const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize)
+ : ScaledFontBase(aUnscaledFont, aSize),
+ mFace(std::move(aFace)),
+ mInstanceData(aPattern) {}
+
+ScaledFontFontconfig::ScaledFontFontconfig(
+ RefPtr<SharedFTFace>&& aFace, const InstanceData& aInstanceData,
+ const RefPtr<UnscaledFont>& aUnscaledFont, Float aSize)
+ : ScaledFontBase(aUnscaledFont, aSize),
+ mFace(std::move(aFace)),
+ mInstanceData(aInstanceData) {}
+
+bool ScaledFontFontconfig::UseSubpixelPosition() const {
+ return !MOZ_UNLIKELY(
+ StaticPrefs::
+ gfx_text_subpixel_position_force_disabled_AtStartup()) &&
+ mInstanceData.mAntialias != AntialiasMode::NONE &&
+ FT_IS_SCALABLE(mFace->GetFace()) &&
+ (mInstanceData.mHinting == FontHinting::NONE ||
+ mInstanceData.mHinting == FontHinting::LIGHT ||
+ MOZ_UNLIKELY(
+ StaticPrefs::
+ gfx_text_subpixel_position_force_enabled_AtStartup()));
+}
+
+SkTypeface* ScaledFontFontconfig::CreateSkTypeface() {
+ SkPixelGeometry geo = mInstanceData.mFlags & InstanceData::SUBPIXEL_BGR
+ ? (mInstanceData.mFlags & InstanceData::LCD_VERTICAL
+ ? kBGR_V_SkPixelGeometry
+ : kBGR_H_SkPixelGeometry)
+ : (mInstanceData.mFlags & InstanceData::LCD_VERTICAL
+ ? kRGB_V_SkPixelGeometry
+ : kRGB_H_SkPixelGeometry);
+ return SkCreateTypefaceFromCairoFTFont(mFace->GetFace(), mFace.get(), geo,
+ mInstanceData.mLcdFilter);
+}
+
+void ScaledFontFontconfig::SetupSkFontDrawOptions(SkFont& aFont) {
+ aFont.setSubpixel(UseSubpixelPosition());
+
+ if (mInstanceData.mFlags & InstanceData::AUTOHINT) {
+ aFont.setForceAutoHinting(true);
+ }
+ if (mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP) {
+ aFont.setEmbeddedBitmaps(true);
+ }
+ if (mInstanceData.mFlags & InstanceData::EMBOLDEN) {
+ aFont.setEmbolden(true);
+ }
+
+ aFont.setHinting(GfxHintingToSkiaHinting(mInstanceData.mHinting));
+}
+
+bool ScaledFontFontconfig::MayUseBitmaps() {
+ return mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP &&
+ !FT_IS_SCALABLE(mFace->GetFace());
+}
+
+cairo_font_face_t* ScaledFontFontconfig::CreateCairoFontFace(
+ cairo_font_options_t* aFontOptions) {
+ int loadFlags;
+ unsigned int synthFlags;
+ mInstanceData.SetupFontOptions(aFontOptions, &loadFlags, &synthFlags);
+
+ return cairo_ft_font_face_create_for_ft_face(mFace->GetFace(), loadFlags,
+ synthFlags, mFace.get());
+}
+
+AntialiasMode ScaledFontFontconfig::GetDefaultAAMode() {
+ return mInstanceData.mAntialias;
+}
+
+bool FcPatternAllowsBitmaps(FcPattern* aPattern, bool aAntialias,
+ bool aHinting) {
+ if (!aAntialias) {
+ // Always allow bitmaps when antialiasing is disabled
+ return true;
+ }
+ FcBool bitmap;
+ if (FcPatternGetBool(aPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) !=
+ FcResultMatch ||
+ !bitmap) {
+ // If bitmaps were explicitly disabled, then disallow them
+ return false;
+ }
+ if (aHinting) {
+ // If hinting is used and bitmaps were enabled, then allow them
+ return true;
+ }
+ // When hinting is disabled, then avoid loading bitmaps from outline
+ // fonts. However, emoji fonts may have no outlines while containing
+ // bitmaps intended to be scaled, so still allow those.
+ FcBool outline;
+ if (FcPatternGetBool(aPattern, FC_OUTLINE, 0, &outline) == FcResultMatch &&
+ outline) {
+ return false;
+ }
+ FcBool scalable;
+ if (FcPatternGetBool(aPattern, FC_SCALABLE, 0, &scalable) != FcResultMatch ||
+ !scalable) {
+ return false;
+ }
+ return true;
+}
+
+ScaledFontFontconfig::InstanceData::InstanceData(FcPattern* aPattern)
+ : mFlags(0),
+ mAntialias(AntialiasMode::NONE),
+ mHinting(FontHinting::NONE),
+ mLcdFilter(FT_LCD_FILTER_LEGACY) {
+ // Record relevant Fontconfig properties into instance data.
+ FcBool autohint;
+ if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch &&
+ autohint) {
+ mFlags |= AUTOHINT;
+ }
+ FcBool embolden;
+ if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch &&
+ embolden) {
+ mFlags |= EMBOLDEN;
+ }
+
+ // For printer fonts, Cairo hint metrics and hinting will be disabled.
+ // For other fonts, allow hint metrics and hinting.
+ FcBool printing;
+ if (FcPatternGetBool(aPattern, "gfx.printing", 0, &printing) !=
+ FcResultMatch ||
+ !printing) {
+ mFlags |= HINT_METRICS;
+
+ FcBool hinting;
+ if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch ||
+ hinting) {
+ int hintstyle;
+ if (FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &hintstyle) !=
+ FcResultMatch) {
+ hintstyle = FC_HINT_FULL;
+ }
+ switch (hintstyle) {
+ case FC_HINT_SLIGHT:
+ mHinting = FontHinting::LIGHT;
+ break;
+ case FC_HINT_MEDIUM:
+ mHinting = FontHinting::NORMAL;
+ break;
+ case FC_HINT_FULL:
+ mHinting = FontHinting::FULL;
+ break;
+ case FC_HINT_NONE:
+ default:
+ break;
+ }
+ }
+ }
+
+ FcBool antialias;
+ if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &antialias) ==
+ FcResultMatch &&
+ !antialias) {
+ // If AA is explicitly disabled, leave bitmaps enabled.
+ mFlags |= EMBEDDED_BITMAP;
+ } else {
+ mAntialias = AntialiasMode::GRAY;
+
+ // Otherwise, if AA is enabled, disable embedded bitmaps unless explicitly
+ // enabled.
+ if (FcPatternAllowsBitmaps(aPattern, true, mHinting != FontHinting::NONE)) {
+ mFlags |= EMBEDDED_BITMAP;
+ }
+
+ // Only record subpixel order and lcd filtering if antialiasing is enabled.
+ int rgba;
+ if (mFlags & HINT_METRICS &&
+ FcPatternGetInteger(aPattern, FC_RGBA, 0, &rgba) == FcResultMatch) {
+ switch (rgba) {
+ case FC_RGBA_RGB:
+ case FC_RGBA_BGR:
+ case FC_RGBA_VRGB:
+ case FC_RGBA_VBGR:
+ mAntialias = AntialiasMode::SUBPIXEL;
+ if (rgba == FC_RGBA_VRGB || rgba == FC_RGBA_VBGR) {
+ mFlags |= LCD_VERTICAL;
+ }
+ if (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR) {
+ mFlags |= SUBPIXEL_BGR;
+ }
+ break;
+ case FC_RGBA_NONE:
+ case FC_RGBA_UNKNOWN:
+ default:
+ break;
+ }
+ }
+
+ int filter;
+ if (mAntialias == AntialiasMode::SUBPIXEL &&
+ FcPatternGetInteger(aPattern, FC_LCD_FILTER, 0, &filter) ==
+ FcResultMatch) {
+ switch (filter) {
+ case FC_LCD_NONE:
+ mLcdFilter = FT_LCD_FILTER_NONE;
+ break;
+ case FC_LCD_DEFAULT:
+ mLcdFilter = FT_LCD_FILTER_DEFAULT;
+ break;
+ case FC_LCD_LIGHT:
+ mLcdFilter = FT_LCD_FILTER_LIGHT;
+ break;
+ case FC_LCD_LEGACY:
+ default:
+ break;
+ }
+ }
+ }
+}
+
+ScaledFontFontconfig::InstanceData::InstanceData(
+ const wr::FontInstanceOptions* aOptions,
+ const wr::FontInstancePlatformOptions* aPlatformOptions)
+ : mFlags(HINT_METRICS),
+ mAntialias(AntialiasMode::NONE),
+ mHinting(FontHinting::FULL),
+ mLcdFilter(FT_LCD_FILTER_LEGACY) {
+ if (aOptions) {
+ if (aOptions->flags & wr::FontInstanceFlags::FORCE_AUTOHINT) {
+ mFlags |= AUTOHINT;
+ }
+ if (aOptions->flags & wr::FontInstanceFlags::EMBEDDED_BITMAPS) {
+ mFlags |= EMBEDDED_BITMAP;
+ }
+ if (aOptions->flags & wr::FontInstanceFlags::SYNTHETIC_BOLD) {
+ mFlags |= EMBOLDEN;
+ }
+ if (aOptions->render_mode == wr::FontRenderMode::Subpixel) {
+ mAntialias = AntialiasMode::SUBPIXEL;
+ if (aOptions->flags & wr::FontInstanceFlags::SUBPIXEL_BGR) {
+ mFlags |= SUBPIXEL_BGR;
+ }
+ if (aOptions->flags & wr::FontInstanceFlags::LCD_VERTICAL) {
+ mFlags |= LCD_VERTICAL;
+ }
+ } else if (aOptions->render_mode != wr::FontRenderMode::Mono) {
+ mAntialias = AntialiasMode::GRAY;
+ }
+ }
+ if (aPlatformOptions) {
+ switch (aPlatformOptions->hinting) {
+ case wr::FontHinting::None:
+ mHinting = FontHinting::NONE;
+ break;
+ case wr::FontHinting::Light:
+ mHinting = FontHinting::LIGHT;
+ break;
+ case wr::FontHinting::Normal:
+ mHinting = FontHinting::NORMAL;
+ break;
+ default:
+ break;
+ }
+ switch (aPlatformOptions->lcd_filter) {
+ case wr::FontLCDFilter::None:
+ mLcdFilter = FT_LCD_FILTER_NONE;
+ break;
+ case wr::FontLCDFilter::Default:
+ mLcdFilter = FT_LCD_FILTER_DEFAULT;
+ break;
+ case wr::FontLCDFilter::Light:
+ mLcdFilter = FT_LCD_FILTER_LIGHT;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void ScaledFontFontconfig::InstanceData::SetupFontOptions(
+ cairo_font_options_t* aFontOptions, int* aOutLoadFlags,
+ unsigned int* aOutSynthFlags) const {
+ // For regular (non-printer) fonts, enable hint metrics as well as hinting
+ // and (possibly subpixel) antialiasing.
+ cairo_font_options_set_hint_metrics(
+ aFontOptions,
+ mFlags & HINT_METRICS ? CAIRO_HINT_METRICS_ON : CAIRO_HINT_METRICS_OFF);
+
+ cairo_hint_style_t hinting;
+ switch (mHinting) {
+ case FontHinting::NONE:
+ hinting = CAIRO_HINT_STYLE_NONE;
+ break;
+ case FontHinting::LIGHT:
+ hinting = CAIRO_HINT_STYLE_SLIGHT;
+ break;
+ case FontHinting::NORMAL:
+ hinting = CAIRO_HINT_STYLE_MEDIUM;
+ break;
+ case FontHinting::FULL:
+ hinting = CAIRO_HINT_STYLE_FULL;
+ break;
+ }
+ cairo_font_options_set_hint_style(aFontOptions, hinting);
+
+ switch (mAntialias) {
+ case AntialiasMode::NONE:
+ cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_NONE);
+ break;
+ case AntialiasMode::GRAY:
+ default:
+ cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_GRAY);
+ break;
+ case AntialiasMode::SUBPIXEL: {
+ cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_SUBPIXEL);
+ cairo_font_options_set_subpixel_order(
+ aFontOptions,
+ mFlags & SUBPIXEL_BGR
+ ? (mFlags & LCD_VERTICAL ? CAIRO_SUBPIXEL_ORDER_VBGR
+ : CAIRO_SUBPIXEL_ORDER_BGR)
+ : (mFlags & LCD_VERTICAL ? CAIRO_SUBPIXEL_ORDER_VRGB
+ : CAIRO_SUBPIXEL_ORDER_RGB));
+ cairo_lcd_filter_t lcdFilter = CAIRO_LCD_FILTER_DEFAULT;
+ switch (mLcdFilter) {
+ case FT_LCD_FILTER_NONE:
+ lcdFilter = CAIRO_LCD_FILTER_NONE;
+ break;
+ case FT_LCD_FILTER_DEFAULT:
+ lcdFilter = CAIRO_LCD_FILTER_FIR5;
+ break;
+ case FT_LCD_FILTER_LIGHT:
+ lcdFilter = CAIRO_LCD_FILTER_FIR3;
+ break;
+ case FT_LCD_FILTER_LEGACY:
+ lcdFilter = CAIRO_LCD_FILTER_INTRA_PIXEL;
+ break;
+ }
+ cairo_font_options_set_lcd_filter(aFontOptions, lcdFilter);
+ break;
+ }
+ }
+
+ // Try to build a sane initial set of Cairo font options based on the
+ // Fontconfig pattern.
+ int loadFlags = FT_LOAD_DEFAULT;
+ unsigned int synthFlags = 0;
+
+ if (!(mFlags & EMBEDDED_BITMAP)) {
+ loadFlags |= FT_LOAD_NO_BITMAP;
+ }
+ if (mFlags & AUTOHINT) {
+ loadFlags |= FT_LOAD_FORCE_AUTOHINT;
+ }
+ if (mFlags & EMBOLDEN) {
+ synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD;
+ }
+
+ *aOutLoadFlags = loadFlags;
+ *aOutSynthFlags = synthFlags;
+}
+
+bool ScaledFontFontconfig::GetFontInstanceData(FontInstanceDataOutput aCb,
+ void* aBaton) {
+ std::vector<FontVariation> variations;
+ if (HasVariationSettings()) {
+ UnscaledFontFreeType::GetVariationSettingsFromFace(&variations,
+ mFace->GetFace());
+ }
+
+ aCb(reinterpret_cast<uint8_t*>(&mInstanceData), sizeof(mInstanceData),
+ variations.data(), variations.size(), aBaton);
+ return true;
+}
+
+bool ScaledFontFontconfig::GetWRFontInstanceOptions(
+ Maybe<wr::FontInstanceOptions>* aOutOptions,
+ Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
+ std::vector<FontVariation>* aOutVariations) {
+ wr::FontInstanceOptions options;
+ options.render_mode = wr::FontRenderMode::Alpha;
+ options.flags = wr::FontInstanceFlags{0};
+ if (UseSubpixelPosition()) {
+ options.flags |= wr::FontInstanceFlags::SUBPIXEL_POSITION;
+ }
+ options.bg_color = wr::ToColorU(DeviceColor());
+ options.synthetic_italics =
+ wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());
+
+ wr::FontInstancePlatformOptions platformOptions;
+ platformOptions.lcd_filter = wr::FontLCDFilter::Legacy;
+ platformOptions.hinting = wr::FontHinting::Normal;
+
+ if (mInstanceData.mFlags & InstanceData::AUTOHINT) {
+ options.flags |= wr::FontInstanceFlags::FORCE_AUTOHINT;
+ }
+ if (mInstanceData.mFlags & InstanceData::EMBOLDEN) {
+ options.flags |= wr::FontInstanceFlags::SYNTHETIC_BOLD;
+ }
+ if (mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP) {
+ options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS;
+ }
+ if (mInstanceData.mAntialias != AntialiasMode::NONE) {
+ if (mInstanceData.mAntialias == AntialiasMode::SUBPIXEL) {
+ options.render_mode = wr::FontRenderMode::Subpixel;
+ platformOptions.hinting = wr::FontHinting::LCD;
+ if (mInstanceData.mFlags & InstanceData::LCD_VERTICAL) {
+ options.flags |= wr::FontInstanceFlags::LCD_VERTICAL;
+ }
+ if (mInstanceData.mFlags & InstanceData::SUBPIXEL_BGR) {
+ options.flags |= wr::FontInstanceFlags::SUBPIXEL_BGR;
+ }
+ }
+
+ switch (mInstanceData.mLcdFilter) {
+ case FT_LCD_FILTER_NONE:
+ platformOptions.lcd_filter = wr::FontLCDFilter::None;
+ break;
+ case FT_LCD_FILTER_DEFAULT:
+ platformOptions.lcd_filter = wr::FontLCDFilter::Default;
+ break;
+ case FT_LCD_FILTER_LIGHT:
+ platformOptions.lcd_filter = wr::FontLCDFilter::Light;
+ break;
+ case FT_LCD_FILTER_LEGACY:
+ default:
+ break;
+ }
+
+ switch (mInstanceData.mHinting) {
+ case FontHinting::NONE:
+ platformOptions.hinting = wr::FontHinting::None;
+ break;
+ case FontHinting::LIGHT:
+ platformOptions.hinting = wr::FontHinting::Light;
+ break;
+ case FontHinting::NORMAL:
+ platformOptions.hinting = wr::FontHinting::Normal;
+ break;
+ case FontHinting::FULL:
+ break;
+ }
+ } else {
+ options.render_mode = wr::FontRenderMode::Mono;
+
+ switch (mInstanceData.mHinting) {
+ case FontHinting::NONE:
+ platformOptions.hinting = wr::FontHinting::None;
+ break;
+ default:
+ platformOptions.hinting = wr::FontHinting::Mono;
+ break;
+ }
+ }
+
+ *aOutOptions = Some(options);
+ *aOutPlatformOptions = Some(platformOptions);
+
+ if (HasVariationSettings()) {
+ UnscaledFontFreeType::GetVariationSettingsFromFace(aOutVariations,
+ mFace->GetFace());
+ }
+
+ return true;
+}
+
+already_AddRefed<ScaledFont> UnscaledFontFontconfig::CreateScaledFont(
+ Float aSize, const uint8_t* aInstanceData, uint32_t aInstanceDataLength,
+ const FontVariation* aVariations, uint32_t aNumVariations) {
+ if (aInstanceDataLength < sizeof(ScaledFontFontconfig::InstanceData)) {
+ gfxWarning() << "Fontconfig scaled font instance data is truncated.";
+ return nullptr;
+ }
+ const ScaledFontFontconfig::InstanceData& instanceData =
+ *reinterpret_cast<const ScaledFontFontconfig::InstanceData*>(
+ aInstanceData);
+
+ RefPtr<SharedFTFace> face(InitFace());
+ if (!face) {
+ gfxWarning() << "Attempted to deserialize Fontconfig scaled font without "
+ "FreeType face";
+ return nullptr;
+ }
+
+ if (aNumVariations > 0 && face->GetData()) {
+ if (RefPtr<SharedFTFace> varFace = face->GetData()->CloneFace()) {
+ face = varFace;
+ }
+ }
+
+ // Only apply variations if we have an explicitly cloned face.
+ if (aNumVariations > 0 && face != GetFace()) {
+ ApplyVariationsToFace(aVariations, aNumVariations, face->GetFace());
+ }
+
+ RefPtr<ScaledFontFontconfig> scaledFont =
+ new ScaledFontFontconfig(std::move(face), instanceData, this, aSize);
+
+ return scaledFont.forget();
+}
+
+already_AddRefed<ScaledFont> UnscaledFontFontconfig::CreateScaledFontFromWRFont(
+ Float aGlyphSize, const wr::FontInstanceOptions* aOptions,
+ const wr::FontInstancePlatformOptions* aPlatformOptions,
+ const FontVariation* aVariations, uint32_t aNumVariations) {
+ ScaledFontFontconfig::InstanceData instanceData(aOptions, aPlatformOptions);
+ return CreateScaledFont(aGlyphSize, reinterpret_cast<uint8_t*>(&instanceData),
+ sizeof(instanceData), aVariations, aNumVariations);
+}
+
+bool ScaledFontFontconfig::HasVariationSettings() {
+ // Check if the FT face has been cloned.
+ return mFace &&
+ mFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS &&
+ mFace != static_cast<UnscaledFontFontconfig*>(mUnscaledFont.get())
+ ->GetFace();
+}
+
+already_AddRefed<UnscaledFont> UnscaledFontFontconfig::CreateFromFontDescriptor(
+ const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) {
+ if (aDataLength == 0) {
+ gfxWarning() << "Fontconfig font descriptor is truncated.";
+ return nullptr;
+ }
+ const char* path = reinterpret_cast<const char*>(aData);
+ RefPtr<UnscaledFont> unscaledFont =
+ new UnscaledFontFontconfig(std::string(path, aDataLength), aIndex);
+ return unscaledFont.forget();
+}
+
+} // namespace mozilla::gfx