Minimal integration with the Skia text shaper module (flutter/engine#9556)

This converts the libtxt Paragraph and ParagraphBuilder classes into
interfaces with Minikin and SkShaper/SkParagraph based implementations.

Use the --enable-skshaper GN flag to select the Skia shaper implementation
at build time.
This commit is contained in:
Jason Simmons 2019-07-10 14:13:55 -07:00 committed by GitHub
parent ea163fd95e
commit 20bc546d69
24 changed files with 1335 additions and 702 deletions

View File

@ -1068,6 +1068,10 @@ FILE: ../../../flutter/third_party/txt/src/minikin/SparseBitSet.cpp
FILE: ../../../flutter/third_party/txt/src/minikin/SparseBitSet.h
FILE: ../../../flutter/third_party/txt/src/minikin/WordBreaker.cpp
FILE: ../../../flutter/third_party/txt/src/minikin/WordBreaker.h
FILE: ../../../flutter/third_party/txt/src/skia/paragraph_builder_skia.cc
FILE: ../../../flutter/third_party/txt/src/skia/paragraph_builder_skia.h
FILE: ../../../flutter/third_party/txt/src/skia/paragraph_skia.cc
FILE: ../../../flutter/third_party/txt/src/skia/paragraph_skia.h
FILE: ../../../flutter/third_party/txt/src/txt/asset_font_manager.cc
FILE: ../../../flutter/third_party/txt/src/txt/asset_font_manager.h
FILE: ../../../flutter/third_party/txt/src/txt/font_asset_provider.cc
@ -1082,12 +1086,15 @@ FILE: ../../../flutter/third_party/txt/src/txt/font_style.h
FILE: ../../../flutter/third_party/txt/src/txt/font_weight.h
FILE: ../../../flutter/third_party/txt/src/txt/paint_record.cc
FILE: ../../../flutter/third_party/txt/src/txt/paint_record.h
FILE: ../../../flutter/third_party/txt/src/txt/paragraph.cc
FILE: ../../../flutter/third_party/txt/src/txt/paragraph.h
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_builder.cc
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_builder.h
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_builder_txt.cc
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_builder_txt.h
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_style.cc
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_style.h
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_txt.cc
FILE: ../../../flutter/third_party/txt/src/txt/paragraph_txt.h
FILE: ../../../flutter/third_party/txt/src/txt/placeholder_run.cc
FILE: ../../../flutter/third_party/txt/src/txt/placeholder_run.h
FILE: ../../../flutter/third_party/txt/src/txt/styled_runs.cc

View File

@ -17,6 +17,9 @@ if (target_cpu == "arm" || target_cpu == "arm64") {
declare_args() {
# The runtime mode ("debug", "profile", or "release")
flutter_runtime_mode = "debug"
# Whether to use the Skia text shaper module
flutter_enable_skshaper = false
}
# feature_defines_list ---------------------------------------------------------

View File

@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/fuchsia/sdk.gni")
import("$flutter_root/common/config.gni")
import("$flutter_root/testing/testing.gni")
source_set("ui") {
@ -126,6 +127,10 @@ source_set("ui") {
"$flutter_root/third_party/txt",
]
if (flutter_enable_skshaper) {
defines = [ "FLUTTER_ENABLE_SKSHAPER" ]
}
if (is_fuchsia) {
sources += [
"compositing/scene_host.cc",

View File

@ -283,9 +283,16 @@ ParagraphBuilder::ParagraphBuilder(
FontCollection& font_collection =
UIDartState::Current()->window()->client()->GetFontCollection();
m_paragraphBuilder = std::make_unique<txt::ParagraphBuilder>(
style, font_collection.GetFontCollection());
} // namespace flutter
#if FLUTTER_ENABLE_SKSHAPER
#define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateSkiaBuilder
#else
#define FLUTTER_PARAGRAPH_BUILDER txt::ParagraphBuilder::CreateTxtBuilder
#endif
m_paragraphBuilder =
FLUTTER_PARAGRAPH_BUILDER(style, font_collection.GetFontCollection());
}
ParagraphBuilder::~ParagraphBuilder() = default;

View File

@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import("$flutter_root/common/config.gni")
declare_args() {
flutter_use_fontconfig = false
}
@ -30,6 +32,9 @@ source_set("txt") {
if (flutter_use_fontconfig) {
defines += [ "FLUTTER_USE_FONTCONFIG" ]
}
if (flutter_enable_skshaper) {
defines += [ "FLUTTER_ENABLE_SKSHAPER" ]
}
sources = [
"src/log/log.cc",
@ -84,12 +89,15 @@ source_set("txt") {
"src/txt/font_weight.h",
"src/txt/paint_record.cc",
"src/txt/paint_record.h",
"src/txt/paragraph.cc",
"src/txt/paragraph.h",
"src/txt/paragraph_builder.cc",
"src/txt/paragraph_builder.h",
"src/txt/paragraph_builder_txt.cc",
"src/txt/paragraph_builder_txt.h",
"src/txt/paragraph_style.cc",
"src/txt/paragraph_style.h",
"src/txt/paragraph_txt.cc",
"src/txt/paragraph_txt.h",
"src/txt/placeholder_run.cc",
"src/txt/placeholder_run.h",
"src/txt/platform.h",
@ -130,6 +138,17 @@ source_set("txt") {
deps += [ "//third_party/fontconfig" ]
}
if (flutter_enable_skshaper) {
sources += [
"src/skia/paragraph_builder_skia.cc",
"src/skia/paragraph_builder_skia.h",
"src/skia/paragraph_skia.cc",
"src/skia/paragraph_skia.h",
]
deps += [ "//third_party/skia/modules/skparagraph" ]
}
if (is_mac || is_ios) {
sources += [ "src/txt/platform_mac.mm" ]
deps += [ "$flutter_root/fml" ]

View File

@ -30,7 +30,7 @@
#include "txt/font_style.h"
#include "txt/font_weight.h"
#include "txt/paragraph.h"
#include "txt/paragraph_builder.h"
#include "txt/paragraph_builder_txt.h"
namespace txt {
@ -45,15 +45,15 @@ static void BM_ParagraphShortLayout(benchmark::State& state) {
txt::TextStyle text_style;
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = builder.Build();
auto paragraph = BuildParagraph(builder);
while (state.KeepRunning()) {
paragraph->SetDirty();
paragraph->Layout(300, true);
paragraph->Layout(300);
}
}
BENCHMARK(BM_ParagraphShortLayout);
@ -87,15 +87,15 @@ static void BM_ParagraphLongLayout(benchmark::State& state) {
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = builder.Build();
auto paragraph = BuildParagraph(builder);
while (state.KeepRunning()) {
paragraph->SetDirty();
paragraph->Layout(300, true);
paragraph->Layout(300);
}
}
BENCHMARK(BM_ParagraphLongLayout);
@ -130,15 +130,15 @@ static void BM_ParagraphJustifyLayout(benchmark::State& state) {
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = builder.Build();
auto paragraph = BuildParagraph(builder);
while (state.KeepRunning()) {
paragraph->SetDirty();
paragraph->Layout(300, true);
paragraph->Layout(300);
}
}
BENCHMARK(BM_ParagraphJustifyLayout);
@ -154,15 +154,15 @@ static void BM_ParagraphManyStylesLayout(benchmark::State& state) {
txt::TextStyle text_style;
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
for (int i = 0; i < 1000; ++i) {
builder.PushStyle(text_style);
builder.AddText(u16_text);
}
auto paragraph = builder.Build();
auto paragraph = BuildParagraph(builder);
while (state.KeepRunning()) {
paragraph->SetDirty();
paragraph->Layout(300, true);
paragraph->Layout(300);
}
}
BENCHMARK(BM_ParagraphManyStylesLayout);
@ -181,15 +181,15 @@ static void BM_ParagraphTextBigO(benchmark::State& state) {
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
auto paragraph = builder.Build();
auto paragraph = BuildParagraph(builder);
while (state.KeepRunning()) {
paragraph->SetDirty();
paragraph->Layout(300, true);
paragraph->Layout(300);
}
state.SetComplexityN(state.range(0));
}
@ -210,16 +210,16 @@ static void BM_ParagraphStylesBigO(benchmark::State& state) {
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
for (int i = 0; i < state.range(0); ++i) {
builder.PushStyle(text_style);
builder.AddText(u16_text);
}
auto paragraph = builder.Build();
auto paragraph = BuildParagraph(builder);
while (state.KeepRunning()) {
paragraph->SetDirty();
paragraph->Layout(300, true);
paragraph->Layout(300);
}
state.SetComplexityN(state.range(0));
}
@ -239,11 +239,11 @@ static void BM_ParagraphPaintSimple(benchmark::State& state) {
txt::TextStyle text_style;
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
builder.PushStyle(text_style);
builder.AddText(u16_text);
auto paragraph = builder.Build();
paragraph->Layout(300, true);
auto paragraph = BuildParagraph(builder);
paragraph->Layout(300);
std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>();
bitmap->allocN32Pixels(1000, 1000);
@ -286,11 +286,11 @@ static void BM_ParagraphPaintLarge(benchmark::State& state) {
txt::TextStyle text_style;
text_style.font_families = std::vector<std::string>(1, "Roboto");
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
builder.PushStyle(text_style);
builder.AddText(u16_text);
auto paragraph = builder.Build();
paragraph->Layout(300, true);
auto paragraph = BuildParagraph(builder);
paragraph->Layout(300);
std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>();
bitmap->allocN32Pixels(1000, 1000);
@ -322,7 +322,7 @@ static void BM_ParagraphPaintDecoration(benchmark::State& state) {
text_style.decoration_style = TextDecorationStyle(kSolid);
text_style.color = SK_ColorBLACK;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
builder.PushStyle(text_style);
builder.AddText(u16_text);
@ -335,8 +335,8 @@ static void BM_ParagraphPaintDecoration(benchmark::State& state) {
builder.PushStyle(text_style);
builder.AddText(u16_text);
auto paragraph = builder.Build();
paragraph->Layout(300, true);
auto paragraph = BuildParagraph(builder);
paragraph->Layout(300);
std::unique_ptr<SkBitmap> bitmap = std::make_unique<SkBitmap>();
bitmap->allocN32Pixels(1000, 1000);

View File

@ -23,7 +23,7 @@
#include "txt/font_style.h"
#include "txt/font_weight.h"
#include "txt/paragraph.h"
#include "txt/paragraph_builder.h"
#include "txt/paragraph_builder_txt.h"
namespace txt {
@ -31,7 +31,7 @@ static void BM_ParagraphBuilderConstruction(benchmark::State& state) {
txt::ParagraphStyle paragraph_style;
auto font_collection = GetTestFontCollection();
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
}
}
BENCHMARK(BM_ParagraphBuilderConstruction);
@ -43,7 +43,7 @@ static void BM_ParagraphBuilderPushStyle(benchmark::State& state) {
text_style.color = SK_ColorBLACK;
auto font_collection = GetTestFontCollection();
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
builder.PushStyle(text_style);
}
}
@ -51,7 +51,7 @@ BENCHMARK(BM_ParagraphBuilderPushStyle);
static void BM_ParagraphBuilderPushPop(benchmark::State& state) {
txt::ParagraphStyle paragraph_style;
txt::ParagraphBuilder builder(paragraph_style, GetTestFontCollection());
txt::ParagraphBuilderTxt builder(paragraph_style, GetTestFontCollection());
txt::TextStyle text_style;
text_style.color = SK_ColorBLACK;
@ -70,7 +70,7 @@ static void BM_ParagraphBuilderAddTextString(benchmark::State& state) {
txt::ParagraphStyle paragraph_style;
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
builder.AddText(text);
}
}
@ -82,7 +82,7 @@ static void BM_ParagraphBuilderAddTextChar(benchmark::State& state) {
txt::ParagraphStyle paragraph_style;
auto font_collection = GetTestFontCollection();
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
builder.AddText(text);
}
}
@ -97,7 +97,7 @@ static void BM_ParagraphBuilderAddTextU16stringShort(benchmark::State& state) {
txt::ParagraphStyle paragraph_style;
auto font_collection = GetTestFontCollection();
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
builder.AddText(u16_text);
}
}
@ -131,7 +131,7 @@ static void BM_ParagraphBuilderAddTextU16stringLong(benchmark::State& state) {
txt::ParagraphStyle paragraph_style;
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
builder.AddText(u16_text);
}
}
@ -150,7 +150,7 @@ static void BM_ParagraphBuilderShortParagraphConstruct(
text_style.color = SK_ColorBLACK;
auto font_collection = GetTestFontCollection();
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();
@ -188,7 +188,7 @@ static void BM_ParagraphBuilderLongParagraphConstruct(benchmark::State& state) {
text_style.color = SK_ColorBLACK;
auto font_collection = GetTestFontCollection();
while (state.KeepRunning()) {
txt::ParagraphBuilder builder(paragraph_style, font_collection);
txt::ParagraphBuilderTxt builder(paragraph_style, font_collection);
builder.PushStyle(text_style);
builder.AddText(u16_text);
builder.Pop();

View File

@ -0,0 +1,164 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "paragraph_builder_skia.h"
#include "paragraph_skia.h"
#include "third_party/skia/modules/skparagraph/include/ParagraphStyle.h"
#include "third_party/skia/modules/skparagraph/include/TextStyle.h"
#include "txt/paragraph_style.h"
namespace skt = skia::textlayout;
namespace txt {
namespace {
// Convert txt::FontWeight values (ranging from 0-8) to SkFontStyle::Weight
// values (ranging from 100-900).
SkFontStyle::Weight GetSkFontStyleWeight(txt::FontWeight font_weight) {
return static_cast<SkFontStyle::Weight>(static_cast<int>(font_weight) * 100 +
100);
}
SkFontStyle MakeSkFontStyle(txt::FontWeight font_weight,
txt::FontStyle font_style) {
return SkFontStyle(
GetSkFontStyleWeight(font_weight), SkFontStyle::Width::kNormal_Width,
font_style == txt::FontStyle::normal ? SkFontStyle::Slant::kUpright_Slant
: SkFontStyle::Slant::kItalic_Slant);
}
skt::ParagraphStyle TxtToSkia(const ParagraphStyle& txt) {
skt::ParagraphStyle skia;
skt::TextStyle text_style;
text_style.setFontStyle(MakeSkFontStyle(txt.font_weight, txt.font_style));
text_style.setFontSize(SkDoubleToScalar(txt.font_size));
text_style.setHeight(SkDoubleToScalar(txt.height));
text_style.setFontFamilies({SkString(txt.font_family.c_str())});
text_style.setLocale(SkString(txt.locale.c_str()));
skia.setTextStyle(text_style);
skt::StrutStyle strut_style;
strut_style.setFontStyle(
MakeSkFontStyle(txt.strut_font_weight, txt.strut_font_style));
strut_style.setFontSize(SkDoubleToScalar(txt.strut_font_size));
strut_style.setHeight(SkDoubleToScalar(txt.strut_height));
std::vector<SkString> strut_fonts;
std::transform(txt.strut_font_families.begin(), txt.strut_font_families.end(),
std::back_inserter(strut_fonts),
[](const std::string& f) { return SkString(f.c_str()); });
strut_style.setFontFamilies(strut_fonts);
strut_style.setLeading(txt.strut_leading);
strut_style.setForceStrutHeight(txt.force_strut_height);
strut_style.setStrutEnabled(txt.strut_enabled);
skia.setStrutStyle(strut_style);
skia.setTextAlign(static_cast<skt::TextAlign>(txt.text_align));
skia.setTextDirection(static_cast<skt::TextDirection>(txt.text_direction));
skia.setMaxLines(txt.max_lines);
skia.setEllipsis(txt.ellipsis);
skia.turnHintingOff();
return skia;
}
skt::TextStyle TxtToSkia(const TextStyle& txt) {
skt::TextStyle skia;
skia.setColor(txt.color);
skia.setDecoration(static_cast<skt::TextDecoration>(txt.decoration));
skia.setDecorationColor(txt.decoration_color);
skia.setDecorationStyle(
static_cast<skt::TextDecorationStyle>(txt.decoration_style));
skia.setDecorationThicknessMultiplier(
SkDoubleToScalar(txt.decoration_thickness_multiplier));
skia.setFontStyle(MakeSkFontStyle(txt.font_weight, txt.font_style));
skia.setTextBaseline(static_cast<skt::TextBaseline>(txt.text_baseline));
std::vector<SkString> skia_fonts;
std::transform(txt.font_families.begin(), txt.font_families.end(),
std::back_inserter(skia_fonts),
[](const std::string& f) { return SkString(f.c_str()); });
skia.setFontFamilies(skia_fonts);
skia.setFontSize(SkDoubleToScalar(txt.font_size));
skia.setLetterSpacing(SkDoubleToScalar(txt.letter_spacing));
skia.setWordSpacing(SkDoubleToScalar(txt.word_spacing));
skia.setHeight(SkDoubleToScalar(txt.height));
skia.setLocale(SkString(txt.locale.c_str()));
if (txt.has_background) {
skia.setBackgroundColor(txt.background);
}
if (txt.has_foreground) {
skia.setForegroundColor(txt.foreground);
}
skia.resetShadows();
for (const txt::TextShadow& txt_shadow : txt.text_shadows) {
skt::TextShadow shadow;
shadow.fOffset = txt_shadow.offset;
shadow.fBlurRadius = txt_shadow.blur_radius;
shadow.fColor = txt_shadow.color;
skia.addShadow(shadow);
}
return skia;
}
} // anonymous namespace
ParagraphBuilderSkia::ParagraphBuilderSkia(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection)
: builder_(skt::ParagraphBuilder::make(
TxtToSkia(style),
font_collection->CreateSktFontCollection())),
base_style_(style.GetTextStyle()) {}
ParagraphBuilderSkia::~ParagraphBuilderSkia() = default;
void ParagraphBuilderSkia::PushStyle(const TextStyle& style) {
builder_->pushStyle(TxtToSkia(style));
txt_style_stack_.push(style);
}
void ParagraphBuilderSkia::Pop() {
builder_->pop();
txt_style_stack_.pop();
}
const TextStyle& ParagraphBuilderSkia::PeekStyle() {
return txt_style_stack_.empty() ? base_style_ : txt_style_stack_.top();
}
void ParagraphBuilderSkia::AddText(const std::u16string& text) {
builder_->addText(text);
}
void ParagraphBuilderSkia::AddPlaceholder(PlaceholderRun& span) {
assert(false);
}
std::unique_ptr<Paragraph> ParagraphBuilderSkia::Build() {
return std::make_unique<ParagraphSkia>(builder_->Build());
}
} // namespace txt

View File

@ -0,0 +1,49 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIB_TXT_SRC_PARAGRAPH_BUILDER_SKIA_H_
#define LIB_TXT_SRC_PARAGRAPH_BUILDER_SKIA_H_
#include "txt/paragraph_builder.h"
#include "third_party/skia/modules/skparagraph/include/ParagraphBuilder.h"
namespace txt {
// Implementation of ParagraphBuilder based on Skia's text layout module.
class ParagraphBuilderSkia : public ParagraphBuilder {
public:
ParagraphBuilderSkia(const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection);
virtual ~ParagraphBuilderSkia();
virtual void PushStyle(const TextStyle& style) override;
virtual void Pop() override;
virtual const TextStyle& PeekStyle() override;
virtual void AddText(const std::u16string& text) override;
virtual void AddPlaceholder(PlaceholderRun& span) override;
virtual std::unique_ptr<Paragraph> Build() override;
private:
std::shared_ptr<skia::textlayout::ParagraphBuilder> builder_;
TextStyle base_style_;
std::stack<TextStyle> txt_style_stack_;
};
} // namespace txt
#endif // LIB_TXT_SRC_PARAGRAPH_BUILDER_SKIA_H_

View File

@ -0,0 +1,105 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "paragraph_skia.h"
namespace txt {
namespace skt = skia::textlayout;
ParagraphSkia::ParagraphSkia(std::unique_ptr<skt::Paragraph> paragraph)
: paragraph_(std::move(paragraph)) {}
double ParagraphSkia::GetMaxWidth() {
return paragraph_->getMaxWidth();
}
double ParagraphSkia::GetHeight() {
return paragraph_->getHeight();
}
double ParagraphSkia::GetLongestLine() {
// TODO: implement
return 0;
}
double ParagraphSkia::GetMinIntrinsicWidth() {
return paragraph_->getMinIntrinsicWidth();
}
double ParagraphSkia::GetMaxIntrinsicWidth() {
return paragraph_->getMaxIntrinsicWidth();
}
double ParagraphSkia::GetAlphabeticBaseline() {
return paragraph_->getAlphabeticBaseline();
}
double ParagraphSkia::GetIdeographicBaseline() {
return paragraph_->getIdeographicBaseline();
}
bool ParagraphSkia::DidExceedMaxLines() {
return paragraph_->didExceedMaxLines();
}
void ParagraphSkia::Layout(double width) {
paragraph_->layout(width);
}
void ParagraphSkia::Paint(SkCanvas* canvas, double x, double y) {
paragraph_->paint(canvas, x, y);
}
std::vector<Paragraph::TextBox> ParagraphSkia::GetRectsForRange(
size_t start,
size_t end,
RectHeightStyle rect_height_style,
RectWidthStyle rect_width_style) {
std::vector<skt::TextBox> skia_boxes = paragraph_->getRectsForRange(
start, end, static_cast<skt::RectHeightStyle>(rect_height_style),
static_cast<skt::RectWidthStyle>(rect_width_style));
std::vector<Paragraph::TextBox> boxes;
for (const skt::TextBox skia_box : skia_boxes) {
boxes.emplace_back(skia_box.rect,
static_cast<TextDirection>(skia_box.direction));
}
return boxes;
}
std::vector<Paragraph::TextBox> ParagraphSkia::GetRectsForPlaceholders() {
// TODO: implement
return {};
}
Paragraph::PositionWithAffinity ParagraphSkia::GetGlyphPositionAtCoordinate(
double dx,
double dy) {
skt::PositionWithAffinity skia_pos =
paragraph_->getGlyphPositionAtCoordinate(dx, dy);
return ParagraphSkia::PositionWithAffinity(
skia_pos.position, static_cast<Affinity>(skia_pos.affinity));
}
Paragraph::Range<size_t> ParagraphSkia::GetWordBoundary(size_t offset) {
skt::SkRange<size_t> range = paragraph_->getWordBoundary(offset);
return Paragraph::Range<size_t>(range.start, range.end);
}
} // namespace txt

View File

@ -0,0 +1,72 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIB_TXT_SRC_PARAGRAPH_SKIA_H_
#define LIB_TXT_SRC_PARAGRAPH_SKIA_H_
#include "txt/paragraph.h"
#include "third_party/skia/modules/skparagraph/include/Paragraph.h"
namespace txt {
// Implementation of Paragraph based on Skia's text layout module.
class ParagraphSkia : public Paragraph {
public:
ParagraphSkia(std::unique_ptr<skia::textlayout::Paragraph> paragraph);
virtual ~ParagraphSkia() = default;
double GetMaxWidth() override;
double GetHeight() override;
double GetLongestLine() override;
double GetMinIntrinsicWidth() override;
double GetMaxIntrinsicWidth() override;
double GetAlphabeticBaseline() override;
double GetIdeographicBaseline() override;
bool DidExceedMaxLines() override;
void Layout(double width) override;
void Paint(SkCanvas* canvas, double x, double y) override;
std::vector<TextBox> GetRectsForRange(
size_t start,
size_t end,
RectHeightStyle rect_height_style,
RectWidthStyle rect_width_style) override;
std::vector<TextBox> GetRectsForPlaceholders() override;
PositionWithAffinity GetGlyphPositionAtCoordinate(double dx,
double dy) override;
Range<size_t> GetWordBoundary(size_t offset) override;
private:
std::unique_ptr<skia::textlayout::Paragraph> paragraph_;
};
} // namespace txt
#endif // LIB_TXT_SRC_PARAGRAPH_SKIA_H_

View File

@ -302,4 +302,25 @@ void FontCollection::ClearFontFamilyCache() {
font_collections_cache_.clear();
}
#if FLUTTER_ENABLE_SKSHAPER
sk_sp<skia::textlayout::FontCollection>
FontCollection::CreateSktFontCollection() {
sk_sp<skia::textlayout::FontCollection> skt_collection =
sk_make_sp<skia::textlayout::FontCollection>();
skt_collection->setDefaultFontManager(default_font_manager_,
GetDefaultFontFamily().c_str());
skt_collection->setAssetFontManager(asset_font_manager_);
skt_collection->setDynamicFontManager(dynamic_font_manager_);
skt_collection->setTestFontManager(test_font_manager_);
if (!enable_font_fallback_) {
skt_collection->disableFontFallback();
}
return skt_collection;
}
#endif // FLUTTER_ENABLE_SKSHAPER
} // namespace txt

View File

@ -27,6 +27,7 @@
#include "third_party/googletest/googletest/include/gtest/gtest_prod.h" // nogncheck
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/modules/skparagraph/include/FontCollection.h"
#include "txt/asset_font_manager.h"
#include "txt/text_style.h"
@ -63,6 +64,13 @@ class FontCollection : public std::enable_shared_from_this<FontCollection> {
// Remove all entries in the font family cache.
void ClearFontFamilyCache();
#if FLUTTER_ENABLE_SKSHAPER
// Construct a Skia text layout FontCollection based on this collection.
sk_sp<skia::textlayout::FontCollection> CreateSktFontCollection();
#endif // FLUTTER_ENABLE_SKSHAPER
private:
struct FamilyKey {
FamilyKey(const std::vector<std::string>& families, const std::string& loc);

View File

@ -17,52 +17,17 @@
#ifndef LIB_TXT_SRC_PARAGRAPH_H_
#define LIB_TXT_SRC_PARAGRAPH_H_
#include <set>
#include <utility>
#include <vector>
#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/macros.h"
#include "font_collection.h"
#include "minikin/LineBreaker.h"
#include "paint_record.h"
#include "paragraph_style.h"
#include "placeholder_run.h"
#include "styled_runs.h"
#include "third_party/googletest/googletest/include/gtest/gtest_prod.h" // nogncheck
#include "third_party/skia/include/core/SkFontMetrics.h"
#include "third_party/skia/include/core/SkRect.h"
#include "utils/WindowsUtils.h"
class SkCanvas;
namespace txt {
using GlyphID = uint32_t;
// Constant with the unicode codepoint for the "Object replacement character".
// Used as a stand-in character for Placeholder boxes.
const int objReplacementChar = 0xFFFC;
// Constant with the unicode codepoint for the "Replacement character". This is
// the character that commonly renders as a black diamond with a white question
// mark. Used to replace non-placeholder instances of 0xFFFC in the text buffer.
const int replacementChar = 0xFFFD;
// Paragraph provides Layout, metrics, and painting capabilities for text. Once
// a Paragraph is constructed with ParagraphBuilder::Build(), an example basic
// workflow can be this:
//
// std::unique_ptr<Paragraph> paragraph = paragraph_builder.Build();
// paragraph->Layout(<somewidthgoeshere>);
// paragraph->Paint(<someSkCanvas>, <xpos>, <ypos>);
// Interface for text layout engines. The original implementation was based on
// the Minikin text layout library used by Android. Another implementation is
// available based on Skia's SkShaper/SkParagraph text layout module.
class Paragraph {
public:
// Constructor. It is highly recommended to construct a paragraph with a
// ParagraphBuilder.
Paragraph();
~Paragraph();
enum Affinity { UPSTREAM, DOWNSTREAM };
// Options for various types of bounding boxes provided by
@ -135,69 +100,57 @@ class Paragraph {
}
};
// Minikin Layout doLayout() and LineBreaker addStyleRun() has an
// O(N^2) (according to benchmarks) time complexity where N is the total
// number of characters. However, this is not significant for reasonably sized
// paragraphs. It is currently recommended to break up very long paragraphs
// (10k+ characters) to ensure speedy layout.
//
// Layout calculates the positioning of all the glyphs. Must call this method
// before Painting and getting any statistics from this class.
void Layout(double width, bool force = false);
// Paints the Laid out text onto the supplied SkCanvas at (x, y) offset from
// the origin. Only valid after Layout() is called.
void Paint(SkCanvas* canvas, double x, double y);
// Getter for paragraph_style_.
const ParagraphStyle& GetParagraphStyle() const;
// Returns the number of characters/unicode characters. AKA text_.size()
size_t TextSize() const;
// Returns the height of the laid out paragraph. NOTE this is not a tight
// bounding height of the glyphs, as some glyphs do not reach as low as they
// can.
double GetHeight() const;
virtual ~Paragraph() = default;
// Returns the width provided in the Layout() method. This is the maximum
// width any line in the laid out paragraph can occupy. We expect that
// GetMaxWidth() >= GetLayoutWidth().
double GetMaxWidth() const;
virtual double GetMaxWidth() = 0;
// Returns the height of the laid out paragraph. NOTE this is not a tight
// bounding height of the glyphs, as some glyphs do not reach as low as they
// can.
virtual double GetHeight() = 0;
// Returns the width of the longest line as found in Layout(), which is
// defined as the horizontal distance from the left edge of the leftmost glyph
// to the right edge of the rightmost glyph. We expect that
// GetLongestLine() <= GetMaxWidth().
double GetLongestLine() const;
virtual double GetLongestLine() = 0;
// Returns the actual max width of the longest line after Layout().
virtual double GetMinIntrinsicWidth() = 0;
// Returns the total width covered by the paragraph without linebreaking.
virtual double GetMaxIntrinsicWidth() = 0;
// Distance from top of paragraph to the Alphabetic baseline of the first
// line. Used for alphabetic fonts (A-Z, a-z, greek, etc.)
double GetAlphabeticBaseline() const;
virtual double GetAlphabeticBaseline() = 0;
// Distance from top of paragraph to the Ideographic baseline of the first
// line. Used for ideographic fonts (Chinese, Japanese, Korean, etc.)
double GetIdeographicBaseline() const;
virtual double GetIdeographicBaseline() = 0;
// Returns the total width covered by the paragraph without linebreaking.
double GetMaxIntrinsicWidth() const;
// Checks if the layout extends past the maximum lines and had to be
// truncated.
virtual bool DidExceedMaxLines() = 0;
// Currently, calculated similarly to as GetLayoutWidth(), however this is not
// nessecarily 100% correct in all cases.
//
// Returns the actual max width of the longest line after Layout().
double GetMinIntrinsicWidth() const;
// Layout calculates the positioning of all the glyphs. Must call this method
// before Painting and getting any statistics from this class.
virtual void Layout(double width) = 0;
// Paints the laid out text onto the supplied SkCanvas at (x, y) offset from
// the origin. Only valid after Layout() is called.
virtual void Paint(SkCanvas* canvas, double x, double y) = 0;
// Returns a vector of bounding boxes that enclose all text between start and
// end glyph indexes, including start and excluding end.
std::vector<TextBox> GetRectsForRange(size_t start,
size_t end,
RectHeightStyle rect_height_style,
RectWidthStyle rect_width_style) const;
// Returns the index of the glyph that corresponds to the provided coordinate,
// with the top left corner as the origin, and +y direction as down.
PositionWithAffinity GetGlyphPositionAtCoordinate(double dx, double dy) const;
virtual std::vector<TextBox> GetRectsForRange(
size_t start,
size_t end,
RectHeightStyle rect_height_style,
RectWidthStyle rect_width_style) = 0;
// Returns a vector of bounding boxes that bound all inline placeholders in
// the paragraph.
@ -208,301 +161,16 @@ class Paragraph {
//
// More granular boxes may be obtained through GetRectsForRange, which will
// return bounds on both text as well as inline placeholders.
std::vector<Paragraph::TextBox> GetRectsForPlaceholders() const;
virtual std::vector<TextBox> GetRectsForPlaceholders() = 0;
// Returns the index of the glyph that corresponds to the provided coordinate,
// with the top left corner as the origin, and +y direction as down.
virtual PositionWithAffinity GetGlyphPositionAtCoordinate(double dx,
double dy) = 0;
// Finds the first and last glyphs that define a word containing the glyph at
// index offset.
Range<size_t> GetWordBoundary(size_t offset) const;
// Returns the number of lines the paragraph takes up. If the text exceeds the
// amount width and maxlines provides, Layout() truncates the extra text from
// the layout and this will return the max lines allowed.
size_t GetLineCount() const;
// Checks if the layout extends past the maximum lines and had to be
// truncated.
bool DidExceedMaxLines() const;
// Sets the needs_layout_ to dirty. When Layout() is called, a new Layout will
// be performed when this is set to true. Can also be used to prevent a new
// Layout from being calculated by setting to false.
void SetDirty(bool dirty = true);
private:
friend class ParagraphBuilder;
FRIEND_TEST(ParagraphTest, SimpleParagraph);
FRIEND_TEST(ParagraphTest, SimpleRedParagraph);
FRIEND_TEST(ParagraphTest, RainbowParagraph);
FRIEND_TEST(ParagraphTest, DefaultStyleParagraph);
FRIEND_TEST(ParagraphTest, BoldParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, LeftAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, RightAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, CenterAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, JustifyAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, JustifyRTL);
FRIEND_TEST(ParagraphTest, DecorationsParagraph);
FRIEND_TEST(ParagraphTest, ItalicsParagraph);
FRIEND_TEST(ParagraphTest, ChineseParagraph);
FRIEND_TEST(ParagraphTest, DISABLED_ArabicParagraph);
FRIEND_TEST(ParagraphTest, SpacingParagraph);
FRIEND_TEST(ParagraphTest, LongWordParagraph);
FRIEND_TEST(ParagraphTest, KernScaleParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, NewlineParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, EmojiParagraph);
FRIEND_TEST(ParagraphTest, HyphenBreakParagraph);
FRIEND_TEST(ParagraphTest, RepeatLayoutParagraph);
FRIEND_TEST(ParagraphTest, Ellipsize);
FRIEND_TEST(ParagraphTest, UnderlineShiftParagraph);
FRIEND_TEST(ParagraphTest, WavyDecorationParagraph);
FRIEND_TEST(ParagraphTest, SimpleShadow);
FRIEND_TEST(ParagraphTest, ComplexShadow);
FRIEND_TEST(ParagraphTest, FontFallbackParagraph);
FRIEND_TEST(ParagraphTest, InlinePlaceholder0xFFFCParagraph);
FRIEND_TEST(ParagraphTest, FontFeaturesParagraph);
// Starting data to layout.
std::vector<uint16_t> text_;
// A vector of PlaceholderRuns, which detail the sizes, positioning and break
// behavior of the empty spaces to leave. Each placeholder span corresponds to
// a 0xFFFC (object replacement character) in text_, which indicates the
// position in the text where the placeholder will occur. There should be an
// equal number of 0xFFFC characters and elements in this vector.
std::vector<PlaceholderRun> inline_placeholders_;
// The indexes of the boxes that correspond to an inline placeholder.
std::vector<size_t> inline_placeholder_boxes_;
// The indexes of instances of 0xFFFC that correspond to placeholders. This is
// necessary since the user may pass in manually entered 0xFFFC values using
// AddText().
std::unordered_set<size_t> obj_replacement_char_indexes_;
StyledRuns runs_;
ParagraphStyle paragraph_style_;
std::shared_ptr<FontCollection> font_collection_;
minikin::LineBreaker breaker_;
mutable std::unique_ptr<icu::BreakIterator> word_breaker_;
struct LineRange {
LineRange(size_t s, size_t e, size_t eew, size_t ein, bool h)
: start(s),
end(e),
end_excluding_whitespace(eew),
end_including_newline(ein),
hard_break(h) {}
size_t start, end;
size_t end_excluding_whitespace;
size_t end_including_newline;
bool hard_break;
};
std::vector<LineRange> line_ranges_;
std::vector<double> line_widths_;
// Stores the result of Layout().
std::vector<PaintRecord> records_;
std::vector<double> line_heights_;
std::vector<double> line_baselines_;
bool did_exceed_max_lines_;
// Strut metrics of zero will have no effect on the layout.
struct StrutMetrics {
double ascent = 0; // Positive value to keep signs clear.
double descent = 0;
double leading = 0;
double half_leading = 0;
double line_height = 0;
bool force_strut = false;
};
StrutMetrics strut_;
// Metrics for use in GetRectsForRange(...);
// Per-line max metrics over all runs in a given line.
std::vector<SkScalar> line_max_spacings_;
std::vector<SkScalar> line_max_descent_;
std::vector<SkScalar> line_max_ascent_;
// Overall left and right extremes over all lines.
double max_right_;
double min_left_;
class BidiRun {
public:
// Constructs a BidiRun with is_ghost defaulted to false.
BidiRun(size_t s, size_t e, TextDirection d, const TextStyle& st)
: start_(s), end_(e), direction_(d), style_(&st), is_ghost_(false) {}
// Constructs a BidiRun with a custom is_ghost flag.
BidiRun(size_t s,
size_t e,
TextDirection d,
const TextStyle& st,
bool is_ghost)
: start_(s), end_(e), direction_(d), style_(&st), is_ghost_(is_ghost) {}
// Constructs a placeholder bidi run.
BidiRun(size_t s,
size_t e,
TextDirection d,
const TextStyle& st,
PlaceholderRun& placeholder)
: start_(s),
end_(e),
direction_(d),
style_(&st),
placeholder_run_(&placeholder) {}
size_t start() const { return start_; }
size_t end() const { return end_; }
size_t size() const { return end_ - start_; }
TextDirection direction() const { return direction_; }
const TextStyle& style() const { return *style_; }
PlaceholderRun* placeholder_run() const { return placeholder_run_; }
bool is_rtl() const { return direction_ == TextDirection::rtl; }
// Tracks if the run represents trailing whitespace.
bool is_ghost() const { return is_ghost_; }
bool is_placeholder_run() const { return placeholder_run_ != nullptr; }
private:
size_t start_, end_;
TextDirection direction_;
const TextStyle* style_;
bool is_ghost_;
PlaceholderRun* placeholder_run_ = nullptr;
};
struct GlyphPosition {
Range<size_t> code_units;
Range<double> x_pos;
GlyphPosition(double x_start,
double x_advance,
size_t code_unit_index,
size_t code_unit_width);
void Shift(double delta);
};
struct GlyphLine {
// Glyph positions sorted by x coordinate.
const std::vector<GlyphPosition> positions;
const size_t total_code_units;
GlyphLine(std::vector<GlyphPosition>&& p, size_t tcu);
};
struct CodeUnitRun {
// Glyph positions sorted by code unit index.
std::vector<GlyphPosition> positions;
Range<size_t> code_units;
Range<double> x_pos;
size_t line_number;
SkFontMetrics font_metrics;
TextDirection direction;
const PlaceholderRun* placeholder_run;
CodeUnitRun(std::vector<GlyphPosition>&& p,
Range<size_t> cu,
Range<double> x,
size_t line,
const SkFontMetrics& metrics,
TextDirection dir,
const PlaceholderRun* placeholder);
void Shift(double delta);
};
// Holds the laid out x positions of each glyph.
std::vector<GlyphLine> glyph_lines_;
// Holds the positions of each range of code units in the text.
// Sorted in code unit index order.
std::vector<CodeUnitRun> code_unit_runs_;
// Holds the positions of the inline placeholders.
std::vector<CodeUnitRun> inline_placeholder_code_unit_runs_;
// The max width of the paragraph as provided in the most recent Layout()
// call.
double width_ = -1.0f;
double longest_line_ = -1.0f;
double max_intrinsic_width_ = 0;
double min_intrinsic_width_ = 0;
double alphabetic_baseline_ = FLT_MAX;
double ideographic_baseline_ = FLT_MAX;
bool needs_layout_ = true;
struct WaveCoordinates {
double x_start;
double y_start;
double x_end;
double y_end;
WaveCoordinates(double x_s, double y_s, double x_e, double y_e)
: x_start(x_s), y_start(y_s), x_end(x_e), y_end(y_e) {}
};
// Passes in the text and Styled Runs. text_ and runs_ will later be passed
// into breaker_ in InitBreaker(), which is called in Layout().
void SetText(std::vector<uint16_t> text, StyledRuns runs);
void SetParagraphStyle(const ParagraphStyle& style);
void SetFontCollection(std::shared_ptr<FontCollection> font_collection);
void SetInlinePlaceholders(
std::vector<PlaceholderRun> inline_placeholders,
std::unordered_set<size_t> obj_replacement_char_indexes);
// Break the text into lines.
bool ComputeLineBreaks();
// Break the text into runs based on LTR/RTL text direction.
bool ComputeBidiRuns(std::vector<BidiRun>* result);
// Calculates and populates strut based on paragraph_style_ strut info.
void ComputeStrut(StrutMetrics* strut, SkFont& font);
// Adjusts the ascent and descent based on the existence and type of
// placeholder. This method sets the proper metrics to achieve the different
// PlaceholderAlignment options.
void ComputePlaceholder(PlaceholderRun* placeholder_run,
double& ascent,
double& descent);
bool IsStrutValid() const;
// Calculate the starting X offset of a line based on the line's width and
// alignment.
double GetLineXOffset(double line_total_advance);
// Creates and draws the decorations onto the canvas.
void PaintDecorations(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset);
// Computes the beziers for a wavy decoration. The results will be
// applied to path.
void ComputeWavyDecoration(SkPath& path,
double x,
double y,
double width,
double thickness);
// Draws the background onto the canvas.
void PaintBackground(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset);
// Draws the shadows onto the canvas.
void PaintShadow(SkCanvas* canvas, const PaintRecord& record, SkPoint offset);
// Obtain a Minikin font collection matching this text style.
std::shared_ptr<minikin::FontCollection> GetMinikinFontCollectionForStyle(
const TextStyle& style);
// Get a default SkTypeface for a text style.
sk_sp<SkTypeface> GetDefaultSkiaTypeface(const TextStyle& style);
FML_DISALLOW_COPY_AND_ASSIGN(Paragraph);
virtual Range<size_t> GetWordBoundary(size_t offset) = 0;
};
} // namespace txt

View File

@ -13,75 +13,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "flutter/fml/logging.h"
#include <list>
#include "paragraph_builder.h"
#include "flutter/third_party/txt/src/skia/paragraph_builder_skia.h"
#include "paragraph_builder_txt.h"
#include "paragraph_style.h"
#include "third_party/icu/source/common/unicode/unistr.h"
namespace txt {
ParagraphBuilder::ParagraphBuilder(
ParagraphStyle style,
std::shared_ptr<FontCollection> font_collection)
: font_collection_(std::move(font_collection)) {
SetParagraphStyle(style);
std::unique_ptr<ParagraphBuilder> ParagraphBuilder::CreateTxtBuilder(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection) {
return std::make_unique<ParagraphBuilderTxt>(style, font_collection);
}
ParagraphBuilder::~ParagraphBuilder() = default;
#if FLUTTER_ENABLE_SKSHAPER
void ParagraphBuilder::SetParagraphStyle(const ParagraphStyle& style) {
paragraph_style_ = style;
paragraph_style_index_ = runs_.AddStyle(style.GetTextStyle());
runs_.StartRun(paragraph_style_index_, text_.size());
std::unique_ptr<ParagraphBuilder> ParagraphBuilder::CreateSkiaBuilder(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection) {
return std::make_unique<ParagraphBuilderSkia>(style, font_collection);
}
void ParagraphBuilder::PushStyle(const TextStyle& style) {
size_t style_index = runs_.AddStyle(style);
style_stack_.push_back(style_index);
runs_.StartRun(style_index, text_.size());
}
void ParagraphBuilder::Pop() {
if (style_stack_.empty())
return;
style_stack_.pop_back();
runs_.StartRun(PeekStyleIndex(), text_.size());
}
size_t ParagraphBuilder::PeekStyleIndex() const {
return style_stack_.size() ? style_stack_.back() : paragraph_style_index_;
}
const TextStyle& ParagraphBuilder::PeekStyle() const {
return runs_.GetStyle(PeekStyleIndex());
}
void ParagraphBuilder::AddText(const std::u16string& text) {
text_.insert(text_.end(), text.begin(), text.end());
}
void ParagraphBuilder::AddPlaceholder(PlaceholderRun& span) {
obj_replacement_char_indexes_.insert(text_.size());
runs_.StartRun(PeekStyleIndex(), text_.size());
AddText(std::u16string(1ull, objReplacementChar));
runs_.StartRun(PeekStyleIndex(), text_.size());
inline_placeholders_.push_back(span);
}
std::unique_ptr<Paragraph> ParagraphBuilder::Build() {
runs_.EndRunIfNeeded(text_.size());
std::unique_ptr<Paragraph> paragraph = std::make_unique<Paragraph>();
paragraph->SetText(std::move(text_), std::move(runs_));
paragraph->SetInlinePlaceholders(std::move(inline_placeholders_),
std::move(obj_replacement_char_indexes_));
paragraph->SetParagraphStyle(paragraph_style_);
paragraph->SetFontCollection(font_collection_);
SetParagraphStyle(paragraph_style_);
return paragraph;
}
#endif // FLUTTER_ENABLE_SKSHAPER
} // namespace txt

View File

@ -25,21 +25,27 @@
#include "paragraph.h"
#include "paragraph_style.h"
#include "placeholder_run.h"
#include "styled_runs.h"
#include "text_style.h"
namespace txt {
class ParagraphBuilder {
public:
ParagraphBuilder(ParagraphStyle style,
std::shared_ptr<FontCollection> font_collection);
static std::unique_ptr<ParagraphBuilder> CreateTxtBuilder(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection);
~ParagraphBuilder();
#if FLUTTER_ENABLE_SKSHAPER
static std::unique_ptr<ParagraphBuilder> CreateSkiaBuilder(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection);
#endif
virtual ~ParagraphBuilder() = default;
// Push a style to the stack. The corresponding text added with AddText will
// use the top-most style.
void PushStyle(const TextStyle& style);
virtual void PushStyle(const TextStyle& style) = 0;
// Remove a style from the stack. Useful to apply different styles to chunks
// of text such as bolding.
@ -52,14 +58,14 @@ class ParagraphBuilder {
//
// builder.Pop();
// builder.AddText(" Back to normal again.");
void Pop();
virtual void Pop() = 0;
// Returns the last TextStyle on the stack.
const TextStyle& PeekStyle() const;
virtual const TextStyle& PeekStyle() = 0;
// Adds text to the builder. Forms the proper runs to use the upper-most style
// on the style_stack_;
void AddText(const std::u16string& text);
virtual void AddText(const std::u16string& text) = 0;
// Pushes the information requried to leave an open space, where Flutter may
// draw a custom placeholder into.
@ -67,33 +73,16 @@ class ParagraphBuilder {
// Internally, this method adds a single object replacement character (0xFFFC)
// and emplaces a new PlaceholderRun instance to the vector of inline
// placeholders.
void AddPlaceholder(PlaceholderRun& span);
void SetParagraphStyle(const ParagraphStyle& style);
virtual void AddPlaceholder(PlaceholderRun& span) = 0;
// Constructs a Paragraph object that can be used to layout and paint the text
// to a SkCanvas.
std::unique_ptr<Paragraph> Build();
virtual std::unique_ptr<Paragraph> Build() = 0;
protected:
ParagraphBuilder() = default;
private:
std::vector<uint16_t> text_;
// A vector of PlaceholderRuns, which detail the sizes, positioning and break
// behavior of the empty spaces to leave. Each placeholder span corresponds to
// a 0xFFFC (object replacement character) in text_, which indicates the
// position in the text where the placeholder will occur. There should be an
// equal number of 0xFFFC characters and elements in this vector.
std::vector<PlaceholderRun> inline_placeholders_;
// The indexes of the obj replacement characters added through
// ParagraphBuilder::addPlaceholder().
std::unordered_set<size_t> obj_replacement_char_indexes_;
std::vector<size_t> style_stack_;
std::shared_ptr<FontCollection> font_collection_;
StyledRuns runs_;
ParagraphStyle paragraph_style_;
size_t paragraph_style_index_;
size_t PeekStyleIndex() const;
FML_DISALLOW_COPY_AND_ASSIGN(ParagraphBuilder);
};

View File

@ -0,0 +1,84 @@
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "paragraph_builder_txt.h"
#include "paragraph_txt.h"
namespace txt {
ParagraphBuilderTxt::ParagraphBuilderTxt(
const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection)
: font_collection_(std::move(font_collection)) {
SetParagraphStyle(style);
}
ParagraphBuilderTxt::~ParagraphBuilderTxt() = default;
void ParagraphBuilderTxt::SetParagraphStyle(const ParagraphStyle& style) {
paragraph_style_ = style;
paragraph_style_index_ = runs_.AddStyle(style.GetTextStyle());
runs_.StartRun(paragraph_style_index_, text_.size());
}
void ParagraphBuilderTxt::PushStyle(const TextStyle& style) {
size_t style_index = runs_.AddStyle(style);
style_stack_.push_back(style_index);
runs_.StartRun(style_index, text_.size());
}
void ParagraphBuilderTxt::Pop() {
if (style_stack_.empty()) {
return;
}
style_stack_.pop_back();
runs_.StartRun(PeekStyleIndex(), text_.size());
}
size_t ParagraphBuilderTxt::PeekStyleIndex() const {
return style_stack_.size() ? style_stack_.back() : paragraph_style_index_;
}
const TextStyle& ParagraphBuilderTxt::PeekStyle() {
return runs_.GetStyle(PeekStyleIndex());
}
void ParagraphBuilderTxt::AddText(const std::u16string& text) {
text_.insert(text_.end(), text.begin(), text.end());
}
void ParagraphBuilderTxt::AddPlaceholder(PlaceholderRun& span) {
obj_replacement_char_indexes_.insert(text_.size());
runs_.StartRun(PeekStyleIndex(), text_.size());
AddText(std::u16string(1ull, objReplacementChar));
runs_.StartRun(PeekStyleIndex(), text_.size());
inline_placeholders_.push_back(span);
}
std::unique_ptr<Paragraph> ParagraphBuilderTxt::Build() {
runs_.EndRunIfNeeded(text_.size());
std::unique_ptr<ParagraphTxt> paragraph = std::make_unique<ParagraphTxt>();
paragraph->SetText(std::move(text_), std::move(runs_));
paragraph->SetInlinePlaceholders(std::move(inline_placeholders_),
std::move(obj_replacement_char_indexes_));
paragraph->SetParagraphStyle(paragraph_style_);
paragraph->SetFontCollection(font_collection_);
SetParagraphStyle(paragraph_style_);
return paragraph;
}
} // namespace txt

View File

@ -0,0 +1,65 @@
/*
* Copyright 2019 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIB_TXT_SRC_PARAGRAPH_BUILDER_TXT_H_
#define LIB_TXT_SRC_PARAGRAPH_BUILDER_TXT_H_
#include "paragraph_builder.h"
#include "styled_runs.h"
namespace txt {
// Implementation of ParagraphBuilder that produces paragraphs backed by the
// Minikin text layout library.
class ParagraphBuilderTxt : public ParagraphBuilder {
public:
ParagraphBuilderTxt(const ParagraphStyle& style,
std::shared_ptr<FontCollection> font_collection);
virtual ~ParagraphBuilderTxt();
virtual void PushStyle(const TextStyle& style) override;
virtual void Pop() override;
virtual const TextStyle& PeekStyle() override;
virtual void AddText(const std::u16string& text) override;
virtual void AddPlaceholder(PlaceholderRun& span) override;
virtual std::unique_ptr<Paragraph> Build() override;
private:
std::vector<uint16_t> text_;
// A vector of PlaceholderRuns, which detail the sizes, positioning and break
// behavior of the empty spaces to leave. Each placeholder span corresponds to
// a 0xFFFC (object replacement character) in text_, which indicates the
// position in the text where the placeholder will occur. There should be an
// equal number of 0xFFFC characters and elements in this vector.
std::vector<PlaceholderRun> inline_placeholders_;
// The indexes of the obj replacement characters added through
// ParagraphBuilder::addPlaceholder().
std::unordered_set<size_t> obj_replacement_char_indexes_;
std::vector<size_t> style_stack_;
std::shared_ptr<FontCollection> font_collection_;
StyledRuns runs_;
ParagraphStyle paragraph_style_;
size_t paragraph_style_index_;
void SetParagraphStyle(const ParagraphStyle& style);
size_t PeekStyleIndex() const;
};
} // namespace txt
#endif // LIB_TXT_SRC_PARAGRAPH_BUILDER_TXT_H_

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "paragraph.h"
#include "paragraph_txt.h"
#include <hb.h>
#include <minikin/Layout.h>
@ -190,27 +190,27 @@ void FindWords(const std::vector<uint16_t>& text,
static const float kDoubleDecorationSpacing = 3.0f;
Paragraph::GlyphPosition::GlyphPosition(double x_start,
double x_advance,
size_t code_unit_index,
size_t code_unit_width)
ParagraphTxt::GlyphPosition::GlyphPosition(double x_start,
double x_advance,
size_t code_unit_index,
size_t code_unit_width)
: code_units(code_unit_index, code_unit_index + code_unit_width),
x_pos(x_start, x_start + x_advance) {}
void Paragraph::GlyphPosition::Shift(double delta) {
void ParagraphTxt::GlyphPosition::Shift(double delta) {
x_pos.Shift(delta);
}
Paragraph::GlyphLine::GlyphLine(std::vector<GlyphPosition>&& p, size_t tcu)
ParagraphTxt::GlyphLine::GlyphLine(std::vector<GlyphPosition>&& p, size_t tcu)
: positions(std::move(p)), total_code_units(tcu) {}
Paragraph::CodeUnitRun::CodeUnitRun(std::vector<GlyphPosition>&& p,
Range<size_t> cu,
Range<double> x,
size_t line,
const SkFontMetrics& metrics,
TextDirection dir,
const PlaceholderRun* placeholder)
ParagraphTxt::CodeUnitRun::CodeUnitRun(std::vector<GlyphPosition>&& p,
Range<size_t> cu,
Range<double> x,
size_t line,
const SkFontMetrics& metrics,
TextDirection dir,
const PlaceholderRun* placeholder)
: positions(std::move(p)),
code_units(cu),
x_pos(x),
@ -219,19 +219,19 @@ Paragraph::CodeUnitRun::CodeUnitRun(std::vector<GlyphPosition>&& p,
direction(dir),
placeholder_run(placeholder) {}
void Paragraph::CodeUnitRun::Shift(double delta) {
void ParagraphTxt::CodeUnitRun::Shift(double delta) {
x_pos.Shift(delta);
for (GlyphPosition& position : positions)
position.Shift(delta);
}
Paragraph::Paragraph() {
ParagraphTxt::ParagraphTxt() {
breaker_.setLocale(icu::Locale(), nullptr);
}
Paragraph::~Paragraph() = default;
ParagraphTxt::~ParagraphTxt() = default;
void Paragraph::SetText(std::vector<uint16_t> text, StyledRuns runs) {
void ParagraphTxt::SetText(std::vector<uint16_t> text, StyledRuns runs) {
needs_layout_ = true;
if (text.size() == 0)
return;
@ -239,7 +239,7 @@ void Paragraph::SetText(std::vector<uint16_t> text, StyledRuns runs) {
runs_ = std::move(runs);
}
void Paragraph::SetInlinePlaceholders(
void ParagraphTxt::SetInlinePlaceholders(
std::vector<PlaceholderRun> inline_placeholders,
std::unordered_set<size_t> obj_replacement_char_indexes) {
needs_layout_ = true;
@ -247,7 +247,7 @@ void Paragraph::SetInlinePlaceholders(
obj_replacement_char_indexes_ = std::move(obj_replacement_char_indexes);
}
bool Paragraph::ComputeLineBreaks() {
bool ParagraphTxt::ComputeLineBreaks() {
line_ranges_.clear();
line_widths_.clear();
max_intrinsic_width_ = 0;
@ -379,7 +379,7 @@ bool Paragraph::ComputeLineBreaks() {
return true;
}
bool Paragraph::ComputeBidiRuns(std::vector<BidiRun>* result) {
bool ParagraphTxt::ComputeBidiRuns(std::vector<BidiRun>* result) {
if (text_.empty())
return true;
@ -496,13 +496,13 @@ bool Paragraph::ComputeBidiRuns(std::vector<BidiRun>* result) {
return true;
}
bool Paragraph::IsStrutValid() const {
bool ParagraphTxt::IsStrutValid() const {
// Font size must be positive.
return (paragraph_style_.strut_enabled &&
paragraph_style_.strut_font_size >= 0);
}
void Paragraph::ComputeStrut(StrutMetrics* strut, SkFont& font) {
void ParagraphTxt::ComputeStrut(StrutMetrics* strut, SkFont& font) {
strut->ascent = 0;
strut->descent = 0;
strut->leading = 0;
@ -567,9 +567,9 @@ void Paragraph::ComputeStrut(StrutMetrics* strut, SkFont& font) {
}
}
void Paragraph::ComputePlaceholder(PlaceholderRun* placeholder_run,
double& ascent,
double& descent) {
void ParagraphTxt::ComputePlaceholder(PlaceholderRun* placeholder_run,
double& ascent,
double& descent) {
if (placeholder_run != nullptr) {
// Calculate how much to shift the ascent and descent to account
// for the baseline choice.
@ -641,10 +641,10 @@ void Paragraph::ComputePlaceholder(PlaceholderRun* placeholder_run,
// -Apply letter spacing, alignment, justification, etc
// -Calculate line vertical layout (ascent, descent, etc)
// -Store per-line metrics
void Paragraph::Layout(double width, bool force) {
void ParagraphTxt::Layout(double width) {
double rounded_width = floor(width);
// Do not allow calling layout multiple times without changing anything.
if (!needs_layout_ && rounded_width == width_ && !force) {
if (!needs_layout_ && rounded_width == width_) {
return;
}
@ -1174,7 +1174,7 @@ void Paragraph::Layout(double width, bool force) {
longest_line_ = max_right_ - min_left_;
}
double Paragraph::GetLineXOffset(double line_total_advance) {
double ParagraphTxt::GetLineXOffset(double line_total_advance) {
if (isinf(width_))
return 0;
@ -1189,56 +1189,56 @@ double Paragraph::GetLineXOffset(double line_total_advance) {
}
}
const ParagraphStyle& Paragraph::GetParagraphStyle() const {
const ParagraphStyle& ParagraphTxt::GetParagraphStyle() const {
return paragraph_style_;
}
double Paragraph::GetAlphabeticBaseline() const {
double ParagraphTxt::GetAlphabeticBaseline() {
// Currently -fAscent
return alphabetic_baseline_;
}
double Paragraph::GetIdeographicBaseline() const {
double ParagraphTxt::GetIdeographicBaseline() {
// TODO(garyq): Currently -fAscent + fUnderlinePosition. Verify this.
return ideographic_baseline_;
}
double Paragraph::GetMaxIntrinsicWidth() const {
double ParagraphTxt::GetMaxIntrinsicWidth() {
return max_intrinsic_width_;
}
double Paragraph::GetMinIntrinsicWidth() const {
double ParagraphTxt::GetMinIntrinsicWidth() {
return min_intrinsic_width_;
}
size_t Paragraph::TextSize() const {
size_t ParagraphTxt::TextSize() const {
return text_.size();
}
double Paragraph::GetHeight() const {
double ParagraphTxt::GetHeight() {
return line_heights_.size() ? line_heights_.back() : 0;
}
double Paragraph::GetMaxWidth() const {
double ParagraphTxt::GetMaxWidth() {
return width_;
}
double Paragraph::GetLongestLine() const {
double ParagraphTxt::GetLongestLine() {
return longest_line_;
}
void Paragraph::SetParagraphStyle(const ParagraphStyle& style) {
void ParagraphTxt::SetParagraphStyle(const ParagraphStyle& style) {
needs_layout_ = true;
paragraph_style_ = style;
}
void Paragraph::SetFontCollection(
void ParagraphTxt::SetFontCollection(
std::shared_ptr<FontCollection> font_collection) {
font_collection_ = std::move(font_collection);
}
std::shared_ptr<minikin::FontCollection>
Paragraph::GetMinikinFontCollectionForStyle(const TextStyle& style) {
ParagraphTxt::GetMinikinFontCollectionForStyle(const TextStyle& style) {
std::string locale;
if (!style.locale.empty()) {
uint32_t language_list_id =
@ -1254,7 +1254,7 @@ Paragraph::GetMinikinFontCollectionForStyle(const TextStyle& style) {
style.font_families, locale);
}
sk_sp<SkTypeface> Paragraph::GetDefaultSkiaTypeface(const TextStyle& style) {
sk_sp<SkTypeface> ParagraphTxt::GetDefaultSkiaTypeface(const TextStyle& style) {
std::shared_ptr<minikin::FontCollection> collection =
GetMinikinFontCollectionForStyle(style);
if (!collection) {
@ -1267,7 +1267,7 @@ sk_sp<SkTypeface> Paragraph::GetDefaultSkiaTypeface(const TextStyle& style) {
// The x,y coordinates will be the very top left corner of the rendered
// paragraph.
void Paragraph::Paint(SkCanvas* canvas, double x, double y) {
void ParagraphTxt::Paint(SkCanvas* canvas, double x, double y) {
SkPoint base_offset = SkPoint::Make(x, y);
SkPaint paint;
// Paint the background first before painting any text to prevent
@ -1291,9 +1291,9 @@ void Paragraph::Paint(SkCanvas* canvas, double x, double y) {
}
}
void Paragraph::PaintDecorations(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset) {
void ParagraphTxt::PaintDecorations(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset) {
if (record.style().decoration == TextDecoration::kNone)
return;
@ -1443,11 +1443,11 @@ void Paragraph::PaintDecorations(SkCanvas* canvas,
}
}
void Paragraph::ComputeWavyDecoration(SkPath& path,
double x,
double y,
double width,
double thickness) {
void ParagraphTxt::ComputeWavyDecoration(SkPath& path,
double x,
double y,
double width,
double thickness) {
int wave_count = 0;
double x_start = 0;
// One full wavelength is 4 * thickness.
@ -1485,9 +1485,9 @@ void Paragraph::ComputeWavyDecoration(SkPath& path,
path.rQuadTo(x1, y1, x2, y2);
}
void Paragraph::PaintBackground(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset) {
void ParagraphTxt::PaintBackground(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset) {
if (!record.style().has_background)
return;
@ -1498,9 +1498,9 @@ void Paragraph::PaintBackground(SkCanvas* canvas,
canvas->drawRect(rect, record.style().background);
}
void Paragraph::PaintShadow(SkCanvas* canvas,
const PaintRecord& record,
SkPoint offset) {
void ParagraphTxt::PaintShadow(SkCanvas* canvas,
const PaintRecord& record,
SkPoint offset) {
if (record.style().text_shadows.size() == 0)
return;
for (TextShadow text_shadow : record.style().text_shadows) {
@ -1519,11 +1519,11 @@ void Paragraph::PaintShadow(SkCanvas* canvas,
}
}
std::vector<Paragraph::TextBox> Paragraph::GetRectsForRange(
std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForRange(
size_t start,
size_t end,
RectHeightStyle rect_height_style,
RectWidthStyle rect_width_style) const {
RectWidthStyle rect_width_style) {
// Struct that holds calculated metrics for each line.
struct LineBoxMetrics {
std::vector<Paragraph::TextBox> boxes;
@ -1742,9 +1742,9 @@ std::vector<Paragraph::TextBox> Paragraph::GetRectsForRange(
return boxes;
}
Paragraph::PositionWithAffinity Paragraph::GetGlyphPositionAtCoordinate(
Paragraph::PositionWithAffinity ParagraphTxt::GetGlyphPositionAtCoordinate(
double dx,
double dy) const {
double dy) {
if (line_heights_.empty())
return PositionWithAffinity(0, DOWNSTREAM);
@ -1803,7 +1803,7 @@ Paragraph::PositionWithAffinity Paragraph::GetGlyphPositionAtCoordinate(
// We don't cache this because since this returns all boxes, it is usually
// unnecessary to call this multiple times in succession.
std::vector<Paragraph::TextBox> Paragraph::GetRectsForPlaceholders() const {
std::vector<Paragraph::TextBox> ParagraphTxt::GetRectsForPlaceholders() {
// Struct that holds calculated metrics for each line.
struct LineBoxMetrics {
std::vector<Paragraph::TextBox> boxes;
@ -1841,7 +1841,7 @@ std::vector<Paragraph::TextBox> Paragraph::GetRectsForPlaceholders() const {
return boxes;
}
Paragraph::Range<size_t> Paragraph::GetWordBoundary(size_t offset) const {
Paragraph::Range<size_t> ParagraphTxt::GetWordBoundary(size_t offset) {
if (text_.size() == 0)
return Range<size_t>(0, 0);
@ -1864,15 +1864,15 @@ Paragraph::Range<size_t> Paragraph::GetWordBoundary(size_t offset) const {
return Range<size_t>(prev_boundary, next_boundary);
}
size_t Paragraph::GetLineCount() const {
size_t ParagraphTxt::GetLineCount() {
return line_heights_.size();
}
bool Paragraph::DidExceedMaxLines() const {
bool ParagraphTxt::DidExceedMaxLines() {
return did_exceed_max_lines_;
}
void Paragraph::SetDirty(bool dirty) {
void ParagraphTxt::SetDirty(bool dirty) {
needs_layout_ = dirty;
}

View File

@ -0,0 +1,400 @@
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef LIB_TXT_SRC_PARAGRAPH_TXT_H_
#define LIB_TXT_SRC_PARAGRAPH_TXT_H_
#include <set>
#include <utility>
#include <vector>
#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/macros.h"
#include "font_collection.h"
#include "minikin/LineBreaker.h"
#include "paint_record.h"
#include "paragraph.h"
#include "paragraph_style.h"
#include "placeholder_run.h"
#include "styled_runs.h"
#include "third_party/googletest/googletest/include/gtest/gtest_prod.h" // nogncheck
#include "third_party/skia/include/core/SkFontMetrics.h"
#include "third_party/skia/include/core/SkRect.h"
#include "utils/WindowsUtils.h"
namespace txt {
using GlyphID = uint32_t;
// Constant with the unicode codepoint for the "Object replacement character".
// Used as a stand-in character for Placeholder boxes.
const int objReplacementChar = 0xFFFC;
// Constant with the unicode codepoint for the "Replacement character". This is
// the character that commonly renders as a black diamond with a white question
// mark. Used to replace non-placeholder instances of 0xFFFC in the text buffer.
const int replacementChar = 0xFFFD;
// Paragraph provides Layout, metrics, and painting capabilities for text. Once
// a Paragraph is constructed with ParagraphBuilder::Build(), an example basic
// workflow can be this:
//
// std::unique_ptr<Paragraph> paragraph = paragraph_builder.Build();
// paragraph->Layout(<somewidthgoeshere>);
// paragraph->Paint(<someSkCanvas>, <xpos>, <ypos>);
class ParagraphTxt : public Paragraph {
public:
// Constructor. It is highly recommended to construct a paragraph with a
// ParagraphBuilder.
ParagraphTxt();
virtual ~ParagraphTxt();
// Minikin Layout doLayout() and LineBreaker addStyleRun() has an
// O(N^2) (according to benchmarks) time complexity where N is the total
// number of characters. However, this is not significant for reasonably sized
// paragraphs. It is currently recommended to break up very long paragraphs
// (10k+ characters) to ensure speedy layout.
virtual void Layout(double width) override;
virtual void Paint(SkCanvas* canvas, double x, double y) override;
// Getter for paragraph_style_.
const ParagraphStyle& GetParagraphStyle() const;
// Returns the number of characters/unicode characters. AKA text_.size()
size_t TextSize() const;
double GetHeight() override;
double GetMaxWidth() override;
double GetLongestLine() override;
double GetAlphabeticBaseline() override;
double GetIdeographicBaseline() override;
double GetMaxIntrinsicWidth() override;
// Currently, calculated similarly to as GetLayoutWidth(), however this is not
// necessarily 100% correct in all cases.
double GetMinIntrinsicWidth() override;
std::vector<TextBox> GetRectsForRange(
size_t start,
size_t end,
RectHeightStyle rect_height_style,
RectWidthStyle rect_width_style) override;
PositionWithAffinity GetGlyphPositionAtCoordinate(double dx,
double dy) override;
std::vector<Paragraph::TextBox> GetRectsForPlaceholders() override;
Range<size_t> GetWordBoundary(size_t offset) override;
// Returns the number of lines the paragraph takes up. If the text exceeds the
// amount width and maxlines provides, Layout() truncates the extra text from
// the layout and this will return the max lines allowed.
size_t GetLineCount();
bool DidExceedMaxLines() override;
// Sets the needs_layout_ to dirty. When Layout() is called, a new Layout will
// be performed when this is set to true. Can also be used to prevent a new
// Layout from being calculated by setting to false.
void SetDirty(bool dirty = true);
private:
friend class ParagraphBuilderTxt;
FRIEND_TEST(ParagraphTest, SimpleParagraph);
FRIEND_TEST(ParagraphTest, SimpleRedParagraph);
FRIEND_TEST(ParagraphTest, RainbowParagraph);
FRIEND_TEST(ParagraphTest, DefaultStyleParagraph);
FRIEND_TEST(ParagraphTest, BoldParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, LeftAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, RightAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, CenterAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, JustifyAlignParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, JustifyRTL);
FRIEND_TEST(ParagraphTest, DecorationsParagraph);
FRIEND_TEST(ParagraphTest, ItalicsParagraph);
FRIEND_TEST(ParagraphTest, ChineseParagraph);
FRIEND_TEST(ParagraphTest, DISABLED_ArabicParagraph);
FRIEND_TEST(ParagraphTest, SpacingParagraph);
FRIEND_TEST(ParagraphTest, LongWordParagraph);
FRIEND_TEST(ParagraphTest, KernScaleParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, NewlineParagraph);
FRIEND_TEST_WINDOWS_DISABLED(ParagraphTest, EmojiParagraph);
FRIEND_TEST(ParagraphTest, HyphenBreakParagraph);
FRIEND_TEST(ParagraphTest, RepeatLayoutParagraph);
FRIEND_TEST(ParagraphTest, Ellipsize);
FRIEND_TEST(ParagraphTest, UnderlineShiftParagraph);
FRIEND_TEST(ParagraphTest, WavyDecorationParagraph);
FRIEND_TEST(ParagraphTest, SimpleShadow);
FRIEND_TEST(ParagraphTest, ComplexShadow);
FRIEND_TEST(ParagraphTest, FontFallbackParagraph);
FRIEND_TEST(ParagraphTest, InlinePlaceholder0xFFFCParagraph);
FRIEND_TEST(ParagraphTest, FontFeaturesParagraph);
// Starting data to layout.
std::vector<uint16_t> text_;
// A vector of PlaceholderRuns, which detail the sizes, positioning and break
// behavior of the empty spaces to leave. Each placeholder span corresponds to
// a 0xFFFC (object replacement character) in text_, which indicates the
// position in the text where the placeholder will occur. There should be an
// equal number of 0xFFFC characters and elements in this vector.
std::vector<PlaceholderRun> inline_placeholders_;
// The indexes of the boxes that correspond to an inline placeholder.
std::vector<size_t> inline_placeholder_boxes_;
// The indexes of instances of 0xFFFC that correspond to placeholders. This is
// necessary since the user may pass in manually entered 0xFFFC values using
// AddText().
std::unordered_set<size_t> obj_replacement_char_indexes_;
StyledRuns runs_;
ParagraphStyle paragraph_style_;
std::shared_ptr<FontCollection> font_collection_;
minikin::LineBreaker breaker_;
mutable std::unique_ptr<icu::BreakIterator> word_breaker_;
struct LineRange {
LineRange(size_t s, size_t e, size_t eew, size_t ein, bool h)
: start(s),
end(e),
end_excluding_whitespace(eew),
end_including_newline(ein),
hard_break(h) {}
size_t start, end;
size_t end_excluding_whitespace;
size_t end_including_newline;
bool hard_break;
};
std::vector<LineRange> line_ranges_;
std::vector<double> line_widths_;
// Stores the result of Layout().
std::vector<PaintRecord> records_;
std::vector<double> line_heights_;
std::vector<double> line_baselines_;
bool did_exceed_max_lines_;
// Strut metrics of zero will have no effect on the layout.
struct StrutMetrics {
double ascent = 0; // Positive value to keep signs clear.
double descent = 0;
double leading = 0;
double half_leading = 0;
double line_height = 0;
bool force_strut = false;
};
StrutMetrics strut_;
// Metrics for use in GetRectsForRange(...);
// Per-line max metrics over all runs in a given line.
std::vector<SkScalar> line_max_spacings_;
std::vector<SkScalar> line_max_descent_;
std::vector<SkScalar> line_max_ascent_;
// Overall left and right extremes over all lines.
double max_right_;
double min_left_;
class BidiRun {
public:
// Constructs a BidiRun with is_ghost defaulted to false.
BidiRun(size_t s, size_t e, TextDirection d, const TextStyle& st)
: start_(s), end_(e), direction_(d), style_(&st), is_ghost_(false) {}
// Constructs a BidiRun with a custom is_ghost flag.
BidiRun(size_t s,
size_t e,
TextDirection d,
const TextStyle& st,
bool is_ghost)
: start_(s), end_(e), direction_(d), style_(&st), is_ghost_(is_ghost) {}
// Constructs a placeholder bidi run.
BidiRun(size_t s,
size_t e,
TextDirection d,
const TextStyle& st,
PlaceholderRun& placeholder)
: start_(s),
end_(e),
direction_(d),
style_(&st),
placeholder_run_(&placeholder) {}
size_t start() const { return start_; }
size_t end() const { return end_; }
size_t size() const { return end_ - start_; }
TextDirection direction() const { return direction_; }
const TextStyle& style() const { return *style_; }
PlaceholderRun* placeholder_run() const { return placeholder_run_; }
bool is_rtl() const { return direction_ == TextDirection::rtl; }
// Tracks if the run represents trailing whitespace.
bool is_ghost() const { return is_ghost_; }
bool is_placeholder_run() const { return placeholder_run_ != nullptr; }
private:
size_t start_, end_;
TextDirection direction_;
const TextStyle* style_;
bool is_ghost_;
PlaceholderRun* placeholder_run_ = nullptr;
};
struct GlyphPosition {
Range<size_t> code_units;
Range<double> x_pos;
GlyphPosition(double x_start,
double x_advance,
size_t code_unit_index,
size_t code_unit_width);
void Shift(double delta);
};
struct GlyphLine {
// Glyph positions sorted by x coordinate.
const std::vector<GlyphPosition> positions;
const size_t total_code_units;
GlyphLine(std::vector<GlyphPosition>&& p, size_t tcu);
};
struct CodeUnitRun {
// Glyph positions sorted by code unit index.
std::vector<GlyphPosition> positions;
Range<size_t> code_units;
Range<double> x_pos;
size_t line_number;
SkFontMetrics font_metrics;
TextDirection direction;
const PlaceholderRun* placeholder_run;
CodeUnitRun(std::vector<GlyphPosition>&& p,
Range<size_t> cu,
Range<double> x,
size_t line,
const SkFontMetrics& metrics,
TextDirection dir,
const PlaceholderRun* placeholder);
void Shift(double delta);
};
// Holds the laid out x positions of each glyph.
std::vector<GlyphLine> glyph_lines_;
// Holds the positions of each range of code units in the text.
// Sorted in code unit index order.
std::vector<CodeUnitRun> code_unit_runs_;
// Holds the positions of the inline placeholders.
std::vector<CodeUnitRun> inline_placeholder_code_unit_runs_;
// The max width of the paragraph as provided in the most recent Layout()
// call.
double width_ = -1.0f;
double longest_line_ = -1.0f;
double max_intrinsic_width_ = 0;
double min_intrinsic_width_ = 0;
double alphabetic_baseline_ = FLT_MAX;
double ideographic_baseline_ = FLT_MAX;
bool needs_layout_ = true;
struct WaveCoordinates {
double x_start;
double y_start;
double x_end;
double y_end;
WaveCoordinates(double x_s, double y_s, double x_e, double y_e)
: x_start(x_s), y_start(y_s), x_end(x_e), y_end(y_e) {}
};
// Passes in the text and Styled Runs. text_ and runs_ will later be passed
// into breaker_ in InitBreaker(), which is called in Layout().
void SetText(std::vector<uint16_t> text, StyledRuns runs);
void SetParagraphStyle(const ParagraphStyle& style);
void SetFontCollection(std::shared_ptr<FontCollection> font_collection);
void SetInlinePlaceholders(
std::vector<PlaceholderRun> inline_placeholders,
std::unordered_set<size_t> obj_replacement_char_indexes);
// Break the text into lines.
bool ComputeLineBreaks();
// Break the text into runs based on LTR/RTL text direction.
bool ComputeBidiRuns(std::vector<BidiRun>* result);
// Calculates and populates strut based on paragraph_style_ strut info.
void ComputeStrut(StrutMetrics* strut, SkFont& font);
// Adjusts the ascent and descent based on the existence and type of
// placeholder. This method sets the proper metrics to achieve the different
// PlaceholderAlignment options.
void ComputePlaceholder(PlaceholderRun* placeholder_run,
double& ascent,
double& descent);
bool IsStrutValid() const;
// Calculate the starting X offset of a line based on the line's width and
// alignment.
double GetLineXOffset(double line_total_advance);
// Creates and draws the decorations onto the canvas.
void PaintDecorations(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset);
// Computes the beziers for a wavy decoration. The results will be
// applied to path.
void ComputeWavyDecoration(SkPath& path,
double x,
double y,
double width,
double thickness);
// Draws the background onto the canvas.
void PaintBackground(SkCanvas* canvas,
const PaintRecord& record,
SkPoint base_offset);
// Draws the shadows onto the canvas.
void PaintShadow(SkCanvas* canvas, const PaintRecord& record, SkPoint offset);
// Obtain a Minikin font collection matching this text style.
std::shared_ptr<minikin::FontCollection> GetMinikinFontCollectionForStyle(
const TextStyle& style);
// Get a default SkTypeface for a text style.
sk_sp<SkTypeface> GetDefaultSkiaTypeface(const TextStyle& style);
FML_DISALLOW_COPY_AND_ASSIGN(ParagraphTxt);
};
} // namespace txt
#endif // LIB_TXT_SRC_PARAGRAPH_TXT_H_

File diff suppressed because it is too large Load Diff

View File

@ -118,4 +118,12 @@ std::shared_ptr<FontCollection> GetTestFontCollection() {
return collection;
}
// Build a paragraph and return it as a ParagraphTxt usable by tests that need
// access to ParagraphTxt internals.
std::unique_ptr<ParagraphTxt> BuildParagraph(
txt::ParagraphBuilderTxt& builder) {
return std::unique_ptr<txt::ParagraphTxt>(
static_cast<txt::ParagraphTxt*>(builder.Build().release()));
}
} // namespace txt

View File

@ -18,6 +18,8 @@
#include "flutter/fml/command_line.h"
#include "txt/font_collection.h"
#include "txt/paragraph_builder_txt.h"
#include "txt/paragraph_txt.h"
namespace txt {
@ -31,4 +33,6 @@ void SetCommandLine(fml::CommandLine cmd);
std::shared_ptr<FontCollection> GetTestFontCollection();
std::unique_ptr<ParagraphTxt> BuildParagraph(ParagraphBuilderTxt& builder);
} // namespace txt

View File

@ -93,6 +93,7 @@ def to_gn_args(args):
gn_args['skia_use_expat'] = args.target_os == 'android'
gn_args['skia_use_fontconfig'] = args.enable_fontconfig
gn_args['flutter_use_fontconfig'] = args.enable_fontconfig
gn_args['flutter_enable_skshaper'] = args.enable_skshaper
gn_args['is_official_build'] = True # Disable Skia test utilities.
gn_args['dart_component_kind'] = 'static_library' # Always link Dart in statically.
gn_args['is_debug'] = args.unoptimized
@ -307,6 +308,7 @@ def parse_args(args):
parser.add_argument('--enable-metal', action='store_true', default=False)
parser.add_argument('--enable-fontconfig', action='store_true', default=False)
parser.add_argument('--enable-skshaper', action='store_true', default=False)
parser.add_argument('--embedder-for-target', dest='embedder_for_target', action='store_true', default=False)