Commit b2775899 authored by Etienne Bergeron's avatar Etienne Bergeron Committed by Commit Bot

Allow multiple GetFallbackFont(...) calls for different runs

This CL has main goal to remove calls to the FontLink (GDI) fallback
fonts.

ShapeRuns is trying to find fonts to shape runs. There are cases where
the runs are grouped together but they can be rendered with different
fonts.

Previously, we were trying to determine the fallback font based on the
text of the first run, which is arbitrary. We observed cases where this
is not true.

As an example:
  // The following text will be split in 3 runs:
  //   1) u+1F3F3 u+FE0F u+FE0F  (Segoe UI Emoji)
  //   2) u+0020                 (Segoe UI)
  //   3) u+1F308 u+20E0 u+20E0  (Segoe UI Symbol)

The current CL is adding a loop around GetFallback(...) to find the
corresponding fallback font for each run using the fast fallback
mechanism. We believe this may deprecate the usage of the FontLink code.

R=robliao@chromium.org,asvitkine@chromium.org
CC=​drott@chromium.org

Bug: 995789
Change-Id: Iced98fa37f20832d7c86c107472e9fb84ec9d277
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1789844Reviewed-by: default avatarAlexei Svitkine <asvitkine@chromium.org>
Reviewed-by: default avatarDominik Röttsches <drott@chromium.org>
Reviewed-by: default avatarRobert Liao <robliao@chromium.org>
Commit-Queue: Etienne Bergeron <etienneb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#697243}
parent c319d27c
......@@ -2001,31 +2001,55 @@ void RenderTextHarfBuzz::ShapeRuns(
}
}
std::string preferred_fallback_family;
// Keep a set of fonts already tried for shaping runs.
std::set<Font, CaseInsensitiveCompare> fallback_fonts_already_tried;
fallback_fonts_already_tried.insert(primary_font);
// Find fallback fonts for the remaining runs using a worklist algorithm. Try
// to shape the first run by using GetFallbackFont(...) and then try shaping
// other runs with the same font. If the first font can't be shaped, remove it
// and continue with the remaining runs until the worklist is empty. The
// fallback font returned by GetFallbackFont(...) depends on the text of the
// run and the results may differ between runs.
std::vector<internal::TextRunHarfBuzz*> remaining_unshaped_runs;
while (!runs.empty()) {
Font fallback_font(primary_font);
bool fallback_found;
internal::TextRunHarfBuzz* current_run = *runs.begin();
{
SCOPED_UMA_HISTOGRAM_LONG_TIMER("RenderTextHarfBuzz.GetFallbackFontTime");
TRACE_EVENT1("ui", "RenderTextHarfBuzz::GetFallbackFont", "script",
TRACE_STR_COPY(uscript_getShortName(font_params.script)));
const base::StringPiece16 run_text(&text[current_run->range.start()],
current_run->range.length());
fallback_found =
GetFallbackFont(primary_font, locale_, run_text, &fallback_font);
}
Font fallback_font(primary_font);
bool fallback_found;
{
SCOPED_UMA_HISTOGRAM_LONG_TIMER("RenderTextHarfBuzz.GetFallbackFontTime");
TRACE_EVENT1("ui", "RenderTextHarfBuzz::GetFallbackFont", "script",
TRACE_STR_COPY(uscript_getShortName(font_params.script)));
const base::StringPiece16 run_text(&text[runs.front()->range.start()],
runs.front()->range.length());
fallback_found =
GetFallbackFont(primary_font, locale_, run_text, &fallback_font);
}
if (fallback_found) {
preferred_fallback_family = fallback_font.GetFontName();
internal::TextRunHarfBuzz::FontParams test_font_params = font_params;
if (test_font_params.SetFontAndRenderParams(
fallback_font, fallback_font.GetFontRenderParams())) {
ShapeRunsWithFont(text, test_font_params, &runs);
if (fallback_found) {
const bool fallback_font_is_untried =
fallback_fonts_already_tried.insert(fallback_font).second;
if (fallback_font_is_untried) {
internal::TextRunHarfBuzz::FontParams test_font_params = font_params;
if (test_font_params.SetFontAndRenderParams(
fallback_font, fallback_font.GetFontRenderParams())) {
ShapeRunsWithFont(text, test_font_params, &runs);
}
}
}
if (runs.empty()) {
RecordShapeRunsFallback(ShapeRunFallback::FALLBACK);
return;
// Remove the first run if not fully shaped with its associated fallback
// font.
if (!runs.empty() && runs[0] == current_run) {
remaining_unshaped_runs.push_back(current_run);
runs.erase(runs.begin());
}
}
runs.swap(remaining_unshaped_runs);
if (runs.empty()) {
RecordShapeRunsFallback(ShapeRunFallback::FALLBACK);
return;
}
std::vector<Font> fallback_font_list;
{
......@@ -2035,10 +2059,10 @@ void RenderTextHarfBuzz::ShapeRuns(
fallback_font_list = GetFallbackFonts(primary_font);
#if defined(OS_WIN)
// Append fonts in the fallback list of the preferred fallback font.
// Append fonts in the fallback list of the fallback fonts.
// TODO(tapted): Investigate whether there's a case that benefits from this
// on Mac.
if (!preferred_fallback_family.empty()) {
for (const auto& fallback_font : fallback_fonts_already_tried) {
std::vector<Font> fallback_fonts = GetFallbackFonts(fallback_font);
fallback_font_list.insert(fallback_font_list.end(),
fallback_fonts.begin(), fallback_fonts.end());
......@@ -2049,10 +2073,9 @@ void RenderTextHarfBuzz::ShapeRuns(
// http://crbug.com/467459. On some Windows configurations the default font
// could be a raster font like System, which would not give us a reasonable
// fallback font list.
if (!base::LowerCaseEqualsASCII(primary_font.GetFontName(), "segoe ui") &&
!base::LowerCaseEqualsASCII(preferred_fallback_family, "segoe ui")) {
std::vector<Font> default_fallback_families =
GetFallbackFonts(Font("Segoe UI", 13));
Font segoe("Segoe UI", 13);
if (!fallback_fonts_already_tried.count(segoe)) {
std::vector<Font> default_fallback_families = GetFallbackFonts(segoe);
fallback_font_list.insert(fallback_font_list.end(),
default_fallback_families.begin(),
default_fallback_families.end());
......@@ -2065,18 +2088,15 @@ void RenderTextHarfBuzz::ShapeRuns(
"RenderTextHarfBuzz.ShapeRunsWithFallbackFontsTime");
TRACE_EVENT1("ui", "RenderTextHarfBuzz::ShapeRunsWithFallbackFonts",
"fonts_count", fallback_font_list.size());
std::set<Font, CaseInsensitiveCompare> fallback_fonts;
// Try shaping with the fallback fonts.
for (const auto& font : fallback_font_list) {
std::string font_name = font.GetFontName();
if (font_name == primary_font.GetFontName() ||
font_name == preferred_fallback_family || fallback_fonts.count(font)) {
const bool fallback_font_is_untried =
fallback_fonts_already_tried.insert(font).second;
if (!fallback_font_is_untried)
continue;
}
fallback_fonts.insert(font);
FontRenderParamsQuery query;
query.families.push_back(font_name);
......
......@@ -4749,6 +4749,39 @@ TEST_F(RenderTextTest, EmojiFlagGlyphCount) {
#endif
}
TEST_F(RenderTextTest, HarfBuzz_ShapeRunsWithMultipleFonts) {
RenderTextHarfBuzz* render_text = GetRenderText();
// The following text will be split in 3 runs:
// 1) u+1F3F3 u+FE0F u+FE0F (Segoe UI Emoji)
// 2) u+0020 (Segoe UI)
// 3) u+1F308 u+20E0 u+20E0 (Segoe UI Symbol)
// The three runs are shape in the same group but are mapped with three
// different fonts.
render_text->SetText(
UTF8ToUTF16(u8"\U0001F3F3\U0000FE0F\U00000020\U0001F308\U000020E0"));
test_api()->EnsureLayout();
std::vector<base::string16> expected;
expected.push_back(WideToUTF16(L"\U0001F3F3\U0000FE0F"));
expected.push_back(WideToUTF16(L" "));
expected.push_back(WideToUTF16(L"\U0001F308\U000020E0"));
EXPECT_EQ(expected, GetRunListStrings());
EXPECT_EQ("[0->2][3][4->6]", GetRunListStructureString());
#if defined(OS_WIN)
std::vector<std::string> expected_fonts;
if (base::win::GetVersion() < base::win::Version::WIN10)
expected_fonts = {"Segoe UI", "Segoe UI", "Segoe UI Symbol"};
else
expected_fonts = {"Segoe UI Emoji", "Segoe UI", "Segoe UI Symbol"};
std::vector<std::string> mapped_fonts;
for (const auto& font_span : render_text->GetFontSpansForTesting())
mapped_fonts.push_back(font_span.first.GetFontName());
EXPECT_EQ(expected_fonts, mapped_fonts);
#endif
}
TEST_F(RenderTextTest, GlyphBounds) {
const char* kTestStrings[] = {"asdf 1234 qwer", "\u0647\u0654",
"\u0645\u0631\u062D\u0628\u0627"};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment