no bug - Import translations from android-l10n r=release a=l10n CLOSED TREE
[gecko.git] / gfx / 2d / ScaledFontFontconfig.cpp
blob01e4b30092d8acb116b855c6c787de54b1f605da
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/. */
7 #include "ScaledFontFontconfig.h"
8 #include "UnscaledFontFreeType.h"
9 #include "Logging.h"
10 #include "mozilla/StaticPrefs_gfx.h"
11 #include "mozilla/webrender/WebRenderTypes.h"
13 #include "skia/include/ports/SkTypeface_cairo.h"
14 #include "HelpersSkia.h"
16 #include <fontconfig/fcfreetype.h>
18 #include FT_LCD_FILTER_H
19 #include FT_MULTIPLE_MASTERS_H
21 namespace mozilla::gfx {
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) {}
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) {}
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()));
50 SkTypeface* ScaledFontFontconfig::CreateSkTypeface() {
51 SkPixelGeometry geo = mInstanceData.mFlags & InstanceData::SUBPIXEL_BGR
52 ? (mInstanceData.mFlags & InstanceData::LCD_VERTICAL
53 ? kBGR_V_SkPixelGeometry
54 : kBGR_H_SkPixelGeometry)
55 : (mInstanceData.mFlags & InstanceData::LCD_VERTICAL
56 ? kRGB_V_SkPixelGeometry
57 : kRGB_H_SkPixelGeometry);
58 return SkCreateTypefaceFromCairoFTFont(mFace->GetFace(), mFace.get(), geo,
59 mInstanceData.mLcdFilter);
62 void ScaledFontFontconfig::SetupSkFontDrawOptions(SkFont& aFont) {
63 aFont.setSubpixel(UseSubpixelPosition());
65 if (mInstanceData.mFlags & InstanceData::AUTOHINT) {
66 aFont.setForceAutoHinting(true);
68 if (mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP) {
69 aFont.setEmbeddedBitmaps(true);
71 if (mInstanceData.mFlags & InstanceData::EMBOLDEN) {
72 aFont.setEmbolden(true);
75 aFont.setHinting(GfxHintingToSkiaHinting(mInstanceData.mHinting));
78 bool ScaledFontFontconfig::MayUseBitmaps() {
79 return mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP &&
80 !FT_IS_SCALABLE(mFace->GetFace());
83 cairo_font_face_t* ScaledFontFontconfig::CreateCairoFontFace(
84 cairo_font_options_t* aFontOptions) {
85 int loadFlags;
86 unsigned int synthFlags;
87 mInstanceData.SetupFontOptions(aFontOptions, &loadFlags, &synthFlags);
89 return cairo_ft_font_face_create_for_ft_face(mFace->GetFace(), loadFlags,
90 synthFlags, mFace.get());
93 AntialiasMode ScaledFontFontconfig::GetDefaultAAMode() {
94 return mInstanceData.mAntialias;
97 bool FcPatternAllowsBitmaps(FcPattern* aPattern, bool aAntialias,
98 bool aHinting) {
99 if (!aAntialias) {
100 // Always allow bitmaps when antialiasing is disabled
101 return true;
103 FcBool bitmap;
104 if (FcPatternGetBool(aPattern, FC_EMBEDDED_BITMAP, 0, &bitmap) !=
105 FcResultMatch ||
106 !bitmap) {
107 // If bitmaps were explicitly disabled, then disallow them
108 return false;
110 if (aHinting) {
111 // If hinting is used and bitmaps were enabled, then allow them
112 return true;
114 // When hinting is disabled, then avoid loading bitmaps from outline
115 // fonts. However, emoji fonts may have no outlines while containing
116 // bitmaps intended to be scaled, so still allow those.
117 FcBool outline;
118 if (FcPatternGetBool(aPattern, FC_OUTLINE, 0, &outline) == FcResultMatch &&
119 outline) {
120 return false;
122 FcBool scalable;
123 if (FcPatternGetBool(aPattern, FC_SCALABLE, 0, &scalable) != FcResultMatch ||
124 !scalable) {
125 return false;
127 return true;
130 ScaledFontFontconfig::InstanceData::InstanceData(FcPattern* aPattern)
131 : mFlags(0),
132 mAntialias(AntialiasMode::NONE),
133 mHinting(FontHinting::NONE),
134 mLcdFilter(FT_LCD_FILTER_LEGACY) {
135 // Record relevant Fontconfig properties into instance data.
136 FcBool autohint;
137 if (FcPatternGetBool(aPattern, FC_AUTOHINT, 0, &autohint) == FcResultMatch &&
138 autohint) {
139 mFlags |= AUTOHINT;
141 FcBool embolden;
142 if (FcPatternGetBool(aPattern, FC_EMBOLDEN, 0, &embolden) == FcResultMatch &&
143 embolden) {
144 mFlags |= EMBOLDEN;
147 // For printer fonts, Cairo hint metrics and hinting will be disabled.
148 // For other fonts, allow hint metrics and hinting.
149 FcBool printing;
150 if (FcPatternGetBool(aPattern, "gfx.printing", 0, &printing) !=
151 FcResultMatch ||
152 !printing) {
153 mFlags |= HINT_METRICS;
155 FcBool hinting;
156 if (FcPatternGetBool(aPattern, FC_HINTING, 0, &hinting) != FcResultMatch ||
157 hinting) {
158 int hintstyle;
159 if (FcPatternGetInteger(aPattern, FC_HINT_STYLE, 0, &hintstyle) !=
160 FcResultMatch) {
161 hintstyle = FC_HINT_FULL;
163 switch (hintstyle) {
164 case FC_HINT_SLIGHT:
165 mHinting = FontHinting::LIGHT;
166 break;
167 case FC_HINT_MEDIUM:
168 mHinting = FontHinting::NORMAL;
169 break;
170 case FC_HINT_FULL:
171 mHinting = FontHinting::FULL;
172 break;
173 case FC_HINT_NONE:
174 default:
175 break;
180 FcBool antialias;
181 if (FcPatternGetBool(aPattern, FC_ANTIALIAS, 0, &antialias) ==
182 FcResultMatch &&
183 !antialias) {
184 // If AA is explicitly disabled, leave bitmaps enabled.
185 mFlags |= EMBEDDED_BITMAP;
186 } else {
187 mAntialias = AntialiasMode::GRAY;
189 // Otherwise, if AA is enabled, disable embedded bitmaps unless explicitly
190 // enabled.
191 if (FcPatternAllowsBitmaps(aPattern, true, mHinting != FontHinting::NONE)) {
192 mFlags |= EMBEDDED_BITMAP;
195 // Only record subpixel order and lcd filtering if antialiasing is enabled.
196 int rgba;
197 if (mFlags & HINT_METRICS &&
198 FcPatternGetInteger(aPattern, FC_RGBA, 0, &rgba) == FcResultMatch) {
199 switch (rgba) {
200 case FC_RGBA_RGB:
201 case FC_RGBA_BGR:
202 case FC_RGBA_VRGB:
203 case FC_RGBA_VBGR:
204 mAntialias = AntialiasMode::SUBPIXEL;
205 if (rgba == FC_RGBA_VRGB || rgba == FC_RGBA_VBGR) {
206 mFlags |= LCD_VERTICAL;
208 if (rgba == FC_RGBA_BGR || rgba == FC_RGBA_VBGR) {
209 mFlags |= SUBPIXEL_BGR;
211 break;
212 case FC_RGBA_NONE:
213 case FC_RGBA_UNKNOWN:
214 default:
215 break;
219 int filter;
220 if (mAntialias == AntialiasMode::SUBPIXEL &&
221 FcPatternGetInteger(aPattern, FC_LCD_FILTER, 0, &filter) ==
222 FcResultMatch) {
223 switch (filter) {
224 case FC_LCD_NONE:
225 mLcdFilter = FT_LCD_FILTER_NONE;
226 break;
227 case FC_LCD_DEFAULT:
228 mLcdFilter = FT_LCD_FILTER_DEFAULT;
229 break;
230 case FC_LCD_LIGHT:
231 mLcdFilter = FT_LCD_FILTER_LIGHT;
232 break;
233 case FC_LCD_LEGACY:
234 default:
235 break;
241 ScaledFontFontconfig::InstanceData::InstanceData(
242 const wr::FontInstanceOptions* aOptions,
243 const wr::FontInstancePlatformOptions* aPlatformOptions)
244 : mFlags(HINT_METRICS),
245 mAntialias(AntialiasMode::NONE),
246 mHinting(FontHinting::FULL),
247 mLcdFilter(FT_LCD_FILTER_LEGACY) {
248 if (aOptions) {
249 if (aOptions->flags & wr::FontInstanceFlags::FORCE_AUTOHINT) {
250 mFlags |= AUTOHINT;
252 if (aOptions->flags & wr::FontInstanceFlags::EMBEDDED_BITMAPS) {
253 mFlags |= EMBEDDED_BITMAP;
255 if (aOptions->flags & wr::FontInstanceFlags::SYNTHETIC_BOLD) {
256 mFlags |= EMBOLDEN;
258 if (aOptions->render_mode == wr::FontRenderMode::Subpixel) {
259 mAntialias = AntialiasMode::SUBPIXEL;
260 if (aOptions->flags & wr::FontInstanceFlags::SUBPIXEL_BGR) {
261 mFlags |= SUBPIXEL_BGR;
263 if (aOptions->flags & wr::FontInstanceFlags::LCD_VERTICAL) {
264 mFlags |= LCD_VERTICAL;
266 } else if (aOptions->render_mode != wr::FontRenderMode::Mono) {
267 mAntialias = AntialiasMode::GRAY;
270 if (aPlatformOptions) {
271 switch (aPlatformOptions->hinting) {
272 case wr::FontHinting::None:
273 mHinting = FontHinting::NONE;
274 break;
275 case wr::FontHinting::Light:
276 mHinting = FontHinting::LIGHT;
277 break;
278 case wr::FontHinting::Normal:
279 mHinting = FontHinting::NORMAL;
280 break;
281 default:
282 break;
284 switch (aPlatformOptions->lcd_filter) {
285 case wr::FontLCDFilter::None:
286 mLcdFilter = FT_LCD_FILTER_NONE;
287 break;
288 case wr::FontLCDFilter::Default:
289 mLcdFilter = FT_LCD_FILTER_DEFAULT;
290 break;
291 case wr::FontLCDFilter::Light:
292 mLcdFilter = FT_LCD_FILTER_LIGHT;
293 break;
294 default:
295 break;
300 void ScaledFontFontconfig::InstanceData::SetupFontOptions(
301 cairo_font_options_t* aFontOptions, int* aOutLoadFlags,
302 unsigned int* aOutSynthFlags) const {
303 // For regular (non-printer) fonts, enable hint metrics as well as hinting
304 // and (possibly subpixel) antialiasing.
305 cairo_font_options_set_hint_metrics(
306 aFontOptions,
307 mFlags & HINT_METRICS ? CAIRO_HINT_METRICS_ON : CAIRO_HINT_METRICS_OFF);
309 cairo_hint_style_t hinting;
310 switch (mHinting) {
311 case FontHinting::NONE:
312 hinting = CAIRO_HINT_STYLE_NONE;
313 break;
314 case FontHinting::LIGHT:
315 hinting = CAIRO_HINT_STYLE_SLIGHT;
316 break;
317 case FontHinting::NORMAL:
318 hinting = CAIRO_HINT_STYLE_MEDIUM;
319 break;
320 case FontHinting::FULL:
321 hinting = CAIRO_HINT_STYLE_FULL;
322 break;
324 cairo_font_options_set_hint_style(aFontOptions, hinting);
326 switch (mAntialias) {
327 case AntialiasMode::NONE:
328 cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_NONE);
329 break;
330 case AntialiasMode::GRAY:
331 default:
332 cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_GRAY);
333 break;
334 case AntialiasMode::SUBPIXEL: {
335 cairo_font_options_set_antialias(aFontOptions, CAIRO_ANTIALIAS_SUBPIXEL);
336 cairo_font_options_set_subpixel_order(
337 aFontOptions,
338 mFlags & SUBPIXEL_BGR
339 ? (mFlags & LCD_VERTICAL ? CAIRO_SUBPIXEL_ORDER_VBGR
340 : CAIRO_SUBPIXEL_ORDER_BGR)
341 : (mFlags & LCD_VERTICAL ? CAIRO_SUBPIXEL_ORDER_VRGB
342 : CAIRO_SUBPIXEL_ORDER_RGB));
343 cairo_lcd_filter_t lcdFilter = CAIRO_LCD_FILTER_DEFAULT;
344 switch (mLcdFilter) {
345 case FT_LCD_FILTER_NONE:
346 lcdFilter = CAIRO_LCD_FILTER_NONE;
347 break;
348 case FT_LCD_FILTER_DEFAULT:
349 lcdFilter = CAIRO_LCD_FILTER_FIR5;
350 break;
351 case FT_LCD_FILTER_LIGHT:
352 lcdFilter = CAIRO_LCD_FILTER_FIR3;
353 break;
354 case FT_LCD_FILTER_LEGACY:
355 lcdFilter = CAIRO_LCD_FILTER_INTRA_PIXEL;
356 break;
358 cairo_font_options_set_lcd_filter(aFontOptions, lcdFilter);
359 break;
363 // Try to build a sane initial set of Cairo font options based on the
364 // Fontconfig pattern.
365 int loadFlags = FT_LOAD_DEFAULT;
366 unsigned int synthFlags = 0;
368 if (!(mFlags & EMBEDDED_BITMAP)) {
369 loadFlags |= FT_LOAD_NO_BITMAP;
371 if (mFlags & AUTOHINT) {
372 loadFlags |= FT_LOAD_FORCE_AUTOHINT;
374 if (mFlags & EMBOLDEN) {
375 synthFlags |= CAIRO_FT_SYNTHESIZE_BOLD;
378 *aOutLoadFlags = loadFlags;
379 *aOutSynthFlags = synthFlags;
382 bool ScaledFontFontconfig::GetFontInstanceData(FontInstanceDataOutput aCb,
383 void* aBaton) {
384 std::vector<FontVariation> variations;
385 if (HasVariationSettings()) {
386 UnscaledFontFreeType::GetVariationSettingsFromFace(&variations,
387 mFace->GetFace());
390 aCb(reinterpret_cast<uint8_t*>(&mInstanceData), sizeof(mInstanceData),
391 variations.data(), variations.size(), aBaton);
392 return true;
395 bool ScaledFontFontconfig::GetWRFontInstanceOptions(
396 Maybe<wr::FontInstanceOptions>* aOutOptions,
397 Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
398 std::vector<FontVariation>* aOutVariations) {
399 wr::FontInstanceOptions options;
400 options.render_mode = wr::FontRenderMode::Alpha;
401 options.flags = wr::FontInstanceFlags{0};
402 if (UseSubpixelPosition()) {
403 options.flags |= wr::FontInstanceFlags::SUBPIXEL_POSITION;
405 options.synthetic_italics =
406 wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());
408 wr::FontInstancePlatformOptions platformOptions;
409 platformOptions.lcd_filter = wr::FontLCDFilter::Legacy;
410 platformOptions.hinting = wr::FontHinting::Normal;
412 if (mInstanceData.mFlags & InstanceData::AUTOHINT) {
413 options.flags |= wr::FontInstanceFlags::FORCE_AUTOHINT;
415 if (mInstanceData.mFlags & InstanceData::EMBOLDEN) {
416 options.flags |= wr::FontInstanceFlags::SYNTHETIC_BOLD;
418 if (mInstanceData.mFlags & InstanceData::EMBEDDED_BITMAP) {
419 options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS;
421 if (mInstanceData.mAntialias != AntialiasMode::NONE) {
422 if (mInstanceData.mAntialias == AntialiasMode::SUBPIXEL) {
423 options.render_mode = wr::FontRenderMode::Subpixel;
424 platformOptions.hinting = wr::FontHinting::LCD;
425 if (mInstanceData.mFlags & InstanceData::LCD_VERTICAL) {
426 options.flags |= wr::FontInstanceFlags::LCD_VERTICAL;
428 if (mInstanceData.mFlags & InstanceData::SUBPIXEL_BGR) {
429 options.flags |= wr::FontInstanceFlags::SUBPIXEL_BGR;
433 switch (mInstanceData.mLcdFilter) {
434 case FT_LCD_FILTER_NONE:
435 platformOptions.lcd_filter = wr::FontLCDFilter::None;
436 break;
437 case FT_LCD_FILTER_DEFAULT:
438 platformOptions.lcd_filter = wr::FontLCDFilter::Default;
439 break;
440 case FT_LCD_FILTER_LIGHT:
441 platformOptions.lcd_filter = wr::FontLCDFilter::Light;
442 break;
443 case FT_LCD_FILTER_LEGACY:
444 default:
445 break;
448 switch (mInstanceData.mHinting) {
449 case FontHinting::NONE:
450 platformOptions.hinting = wr::FontHinting::None;
451 break;
452 case FontHinting::LIGHT:
453 platformOptions.hinting = wr::FontHinting::Light;
454 break;
455 case FontHinting::NORMAL:
456 platformOptions.hinting = wr::FontHinting::Normal;
457 break;
458 case FontHinting::FULL:
459 break;
461 } else {
462 options.render_mode = wr::FontRenderMode::Mono;
464 switch (mInstanceData.mHinting) {
465 case FontHinting::NONE:
466 platformOptions.hinting = wr::FontHinting::None;
467 break;
468 default:
469 platformOptions.hinting = wr::FontHinting::Mono;
470 break;
474 *aOutOptions = Some(options);
475 *aOutPlatformOptions = Some(platformOptions);
477 if (HasVariationSettings()) {
478 UnscaledFontFreeType::GetVariationSettingsFromFace(aOutVariations,
479 mFace->GetFace());
482 return true;
485 already_AddRefed<ScaledFont> UnscaledFontFontconfig::CreateScaledFont(
486 Float aSize, const uint8_t* aInstanceData, uint32_t aInstanceDataLength,
487 const FontVariation* aVariations, uint32_t aNumVariations) {
488 if (aInstanceDataLength < sizeof(ScaledFontFontconfig::InstanceData)) {
489 gfxWarning() << "Fontconfig scaled font instance data is truncated.";
490 return nullptr;
492 const ScaledFontFontconfig::InstanceData& instanceData =
493 *reinterpret_cast<const ScaledFontFontconfig::InstanceData*>(
494 aInstanceData);
496 RefPtr<SharedFTFace> face(InitFace());
497 if (!face) {
498 gfxWarning() << "Attempted to deserialize Fontconfig scaled font without "
499 "FreeType face";
500 return nullptr;
503 if (aNumVariations > 0 && face->GetData()) {
504 if (RefPtr<SharedFTFace> varFace = face->GetData()->CloneFace()) {
505 face = varFace;
509 // Only apply variations if we have an explicitly cloned face.
510 if (aNumVariations > 0 && face != GetFace()) {
511 ApplyVariationsToFace(aVariations, aNumVariations, face->GetFace());
514 RefPtr<ScaledFontFontconfig> scaledFont =
515 new ScaledFontFontconfig(std::move(face), instanceData, this, aSize);
517 return scaledFont.forget();
520 already_AddRefed<ScaledFont> UnscaledFontFontconfig::CreateScaledFontFromWRFont(
521 Float aGlyphSize, const wr::FontInstanceOptions* aOptions,
522 const wr::FontInstancePlatformOptions* aPlatformOptions,
523 const FontVariation* aVariations, uint32_t aNumVariations) {
524 ScaledFontFontconfig::InstanceData instanceData(aOptions, aPlatformOptions);
525 return CreateScaledFont(aGlyphSize, reinterpret_cast<uint8_t*>(&instanceData),
526 sizeof(instanceData), aVariations, aNumVariations);
529 bool ScaledFontFontconfig::HasVariationSettings() {
530 // Check if the FT face has been cloned.
531 return mFace &&
532 mFace->GetFace()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS &&
533 mFace != static_cast<UnscaledFontFontconfig*>(mUnscaledFont.get())
534 ->GetFace();
537 already_AddRefed<UnscaledFont> UnscaledFontFontconfig::CreateFromFontDescriptor(
538 const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) {
539 if (aDataLength == 0) {
540 gfxWarning() << "Fontconfig font descriptor is truncated.";
541 return nullptr;
543 const char* path = reinterpret_cast<const char*>(aData);
544 RefPtr<UnscaledFont> unscaledFont =
545 new UnscaledFontFontconfig(std::string(path, aDataLength), aIndex);
546 return unscaledFont.forget();
549 } // namespace mozilla::gfx