Reverts  https://github.com/flutter/engine/pull/43533
Cause of https://github.com/flutter/flutter/issues/130476

I was trying to add a test for this but it's taking me a while to get a working test so here's the revert.
This commit is contained in:
Dan Field
2023-07-13 15:13:45 -07:00
committed by GitHub
parent b69b3b6f9e
commit 6fdde7e901
28 changed files with 199 additions and 248 deletions

View File

@@ -2877,35 +2877,6 @@ TEST_P(AiksTest, CanCanvasDrawPicture) {
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, DrawPictureWithText) {
Canvas subcanvas;
ASSERT_TRUE(RenderTextInCanvas(
GetContext(), subcanvas,
"the quick brown fox jumped over the lazy dog!.?", "Roboto-Regular.ttf"));
subcanvas.Translate({0, 10});
subcanvas.Scale(Vector2(3, 3));
ASSERT_TRUE(RenderTextInCanvas(
GetContext(), subcanvas,
"the quick brown fox jumped over the very big lazy dog!.?",
"Roboto-Regular.ttf"));
auto picture = subcanvas.EndRecordingAsPicture();
Canvas canvas;
canvas.Scale(Vector2(.2, .2));
canvas.Save();
canvas.Translate({200, 200});
canvas.Scale(Vector2(3.5, 3.5)); // The text must not be blurry after this.
canvas.DrawPicture(picture);
canvas.Restore();
canvas.Scale(Vector2(1.5, 1.5));
ASSERT_TRUE(RenderTextInCanvas(
GetContext(), canvas,
"the quick brown fox jumped over the smaller lazy dog!.?",
"Roboto-Regular.ttf"));
ASSERT_TRUE(OpenPlaygroundHere(canvas.EndRecordingAsPicture()));
}
TEST_P(AiksTest, MatrixBackdropFilter) {
Canvas canvas;
canvas.SaveLayer({}, std::nullopt,

View File

@@ -42,6 +42,7 @@ void Canvas::Initialize(std::optional<Rect> cull_rect) {
base_pass_ = std::make_unique<EntityPass>();
current_pass_ = base_pass_.get();
xformation_stack_.emplace_back(CanvasStackEntry{.cull_rect = cull_rect});
lazy_glyph_atlas_ = std::make_shared<LazyGlyphAtlas>();
FML_DCHECK(GetSaveCount() == 1u);
FML_DCHECK(base_pass_->GetSubpassesDepth() == 1u);
}
@@ -50,6 +51,7 @@ void Canvas::Reset() {
base_pass_ = nullptr;
current_pass_ = nullptr;
xformation_stack_ = {};
lazy_glyph_atlas_ = nullptr;
}
void Canvas::Save() {
@@ -514,12 +516,15 @@ void Canvas::SaveLayer(const Paint& paint,
void Canvas::DrawTextFrame(const TextFrame& text_frame,
Point position,
const Paint& paint) {
lazy_glyph_atlas_->AddTextFrame(text_frame);
Entity entity;
entity.SetStencilDepth(GetStencilDepth());
entity.SetBlendMode(paint.blend_mode);
auto text_contents = std::make_shared<TextContents>();
text_contents->SetTextFrame(text_frame);
text_contents->SetGlyphAtlas(lazy_glyph_atlas_);
if (paint.color_source.GetType() != ColorSource::Type::kColor) {
auto color_text_contents = std::make_shared<ColorSourceTextContents>();

View File

@@ -162,6 +162,7 @@ class Canvas {
std::unique_ptr<EntityPass> base_pass_;
EntityPass* current_pass_ = nullptr;
std::deque<CanvasStackEntry> xformation_stack_;
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
std::optional<Rect> initial_cull_rect_;
void Initialize(std::optional<Rect> cull_rect);

View File

@@ -1104,7 +1104,8 @@ void DlDispatcher::drawDisplayList(
void DlDispatcher::drawTextBlob(const sk_sp<SkTextBlob> blob,
SkScalar x,
SkScalar y) {
const auto text_frame = TextFrameFromTextBlob(blob);
Scalar scale = canvas_.GetCurrentTransformation().GetMaxBasisLengthXY();
const auto text_frame = TextFrameFromTextBlob(blob, scale);
if (paint_.style == Paint::Style::kStroke) {
auto path = skia_conversions::PathDataFromTextBlob(blob);
auto bounds = text_frame.GetBounds();

View File

@@ -4,7 +4,6 @@
#include "impeller/entity/contents/color_source_text_contents.h"
#include "color_source_text_contents.h"
#include "impeller/entity/contents/content_context.h"
#include "impeller/entity/contents/texture_contents.h"
#include "impeller/renderer/render_pass.h"
@@ -34,12 +33,6 @@ void ColorSourceTextContents::SetTextPosition(Point position) {
position_ = position;
}
void ColorSourceTextContents::PopulateGlyphAtlas(
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
Scalar scale) const {
text_contents_->PopulateGlyphAtlas(lazy_glyph_atlas, scale);
}
bool ColorSourceTextContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {

View File

@@ -32,11 +32,6 @@ class ColorSourceTextContents final : public Contents {
// |Contents|
std::optional<Rect> GetCoverage(const Entity& entity) const override;
// |Contents|
void PopulateGlyphAtlas(
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
Scalar scale) const override;
// |Contents|
bool Render(const ContentContext& renderer,
const Entity& entity,

View File

@@ -696,18 +696,8 @@ class ContentContext {
const SubpassCallback& subpass_callback,
bool msaa_enabled = true) const;
void SetLazyGlyphAtlas(
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas) {
lazy_glyph_atlas_ = lazy_glyph_atlas;
}
std::shared_ptr<LazyGlyphAtlas> GetLazyGlyphAtlas() const {
return lazy_glyph_atlas_;
}
private:
std::shared_ptr<Context> context_;
std::shared_ptr<LazyGlyphAtlas> lazy_glyph_atlas_;
template <class T>
using Variants = std::unordered_map<ContentContextOptions,

View File

@@ -14,7 +14,6 @@
#include "impeller/geometry/color.h"
#include "impeller/geometry/rect.h"
#include "impeller/renderer/snapshot.h"
#include "impeller/typographer/lazy_glyph_atlas.h"
namespace impeller {
@@ -54,11 +53,6 @@ class Contents {
virtual ~Contents();
/// @brief Add any text data to the specified lazy atlas.
virtual void PopulateGlyphAtlas(
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
Scalar scale) const {}
virtual bool Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const = 0;

View File

@@ -29,15 +29,18 @@ void TextContents::SetTextFrame(const TextFrame& frame) {
frame_ = frame;
}
void TextContents::SetGlyphAtlas(std::shared_ptr<LazyGlyphAtlas> atlas) {
lazy_atlas_ = std::move(atlas);
}
std::shared_ptr<GlyphAtlas> TextContents::ResolveAtlas(
GlyphAtlas::Type type,
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
std::shared_ptr<GlyphAtlasContext> atlas_context,
std::shared_ptr<Context> context) const {
FML_DCHECK(lazy_atlas);
if (lazy_atlas) {
return lazy_atlas->CreateOrGetGlyphAtlas(type, std::move(atlas_context),
std::move(context));
FML_DCHECK(lazy_atlas_);
if (lazy_atlas_) {
return lazy_atlas_->CreateOrGetGlyphAtlas(type, std::move(atlas_context),
std::move(context));
}
return nullptr;
@@ -75,43 +78,14 @@ std::optional<Rect> TextContents::GetCoverage(const Entity& entity) const {
return bounds->TransformBounds(entity.GetTransformation());
}
void TextContents::PopulateGlyphAtlas(
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
Scalar scale) const {
lazy_glyph_atlas->AddTextFrame(frame_, scale);
}
bool TextContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto color = GetColor();
if (color.IsTransparent()) {
return true;
}
auto type = frame_.GetAtlasType();
auto scale = entity.DeriveTextScale();
auto atlas =
ResolveAtlas(type, renderer.GetLazyGlyphAtlas(),
renderer.GetGlyphAtlasContext(type), renderer.GetContext());
if (!atlas || !atlas->IsValid()) {
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
return false;
}
// Information shared by all glyph draw calls.
Command cmd;
cmd.label = "TextFrame";
auto opts = OptionsFromPassAndEntity(pass, entity);
opts.primitive_type = PrimitiveType::kTriangle;
if (type == GlyphAtlas::Type::kAlphaBitmap) {
cmd.pipeline = renderer.GetGlyphAtlasPipeline(opts);
} else {
cmd.pipeline = renderer.GetGlyphAtlasColorPipeline(opts);
}
cmd.stencil_reference = entity.GetStencilDepth();
static bool CommonRender(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass,
const Color& color,
const TextFrame& frame,
Vector2 offset,
const std::shared_ptr<GlyphAtlas>& atlas,
Command& cmd) {
using VS = GlyphAtlasPipeline::VertexShader;
using FS = GlyphAtlasPipeline::FragmentShader;
@@ -121,7 +95,7 @@ bool TextContents::Render(const ContentContext& renderer,
frame_info.atlas_size =
Vector2{static_cast<Scalar>(atlas->GetTexture()->GetSize().width),
static_cast<Scalar>(atlas->GetTexture()->GetSize().height)};
frame_info.offset = offset_;
frame_info.offset = offset;
frame_info.is_translation_scale =
entity.GetTransformation().IsTranslationScaleOnly();
frame_info.entity_transform = entity.GetTransformation();
@@ -167,7 +141,7 @@ bool TextContents::Render(const ContentContext& renderer,
auto& host_buffer = pass.GetTransientsBuffer();
size_t vertex_count = 0;
for (const auto& run : frame_.GetRuns()) {
for (const auto& run : frame.GetRuns()) {
vertex_count += run.GetGlyphPositions().size();
}
vertex_count *= 6;
@@ -177,10 +151,10 @@ bool TextContents::Render(const ContentContext& renderer,
[&](uint8_t* contents) {
VS::PerVertexData vtx;
size_t vertex_offset = 0;
for (const auto& run : frame_.GetRuns()) {
for (const auto& run : frame.GetRuns()) {
const Font& font = run.GetFont();
for (const auto& glyph_position : run.GetGlyphPositions()) {
FontGlyphPair font_glyph_pair{font, glyph_position.glyph, scale};
FontGlyphPair font_glyph_pair{font, glyph_position.glyph};
auto maybe_atlas_glyph_bounds =
atlas->FindFontGlyphBounds(font_glyph_pair);
if (!maybe_atlas_glyph_bounds.has_value()) {
@@ -217,4 +191,37 @@ bool TextContents::Render(const ContentContext& renderer,
return pass.AddCommand(cmd);
}
bool TextContents::Render(const ContentContext& renderer,
const Entity& entity,
RenderPass& pass) const {
auto color = GetColor();
if (color.IsTransparent()) {
return true;
}
auto type = frame_.GetAtlasType();
auto atlas = ResolveAtlas(type, renderer.GetGlyphAtlasContext(type),
renderer.GetContext());
if (!atlas || !atlas->IsValid()) {
VALIDATION_LOG << "Cannot render glyphs without prepared atlas.";
return false;
}
// Information shared by all glyph draw calls.
Command cmd;
cmd.label = "TextFrame";
auto opts = OptionsFromPassAndEntity(pass, entity);
opts.primitive_type = PrimitiveType::kTriangle;
if (type == GlyphAtlas::Type::kAlphaBitmap) {
cmd.pipeline = renderer.GetGlyphAtlasPipeline(opts);
} else {
cmd.pipeline = renderer.GetGlyphAtlasColorPipeline(opts);
}
cmd.stencil_reference = entity.GetStencilDepth();
return CommonRender(renderer, entity, pass, color, frame_, offset_, atlas,
cmd);
}
} // namespace impeller

View File

@@ -28,14 +28,14 @@ class TextContents final : public Contents {
void SetTextFrame(const TextFrame& frame);
void SetGlyphAtlas(std::shared_ptr<LazyGlyphAtlas> atlas);
void SetColor(Color color);
Color GetColor() const;
// |Contents|
bool CanInheritOpacity(const Entity& entity) const override;
// |Contents|
void SetInheritedOpacity(Scalar opacity) override;
void SetOffset(Vector2 offset);
@@ -45,11 +45,6 @@ class TextContents final : public Contents {
// |Contents|
std::optional<Rect> GetCoverage(const Entity& entity) const override;
// |Contents|
void PopulateGlyphAtlas(
const std::shared_ptr<LazyGlyphAtlas>& lazy_glyph_atlas,
Scalar scale) const override;
// |Contents|
bool Render(const ContentContext& renderer,
const Entity& entity,
@@ -59,11 +54,11 @@ class TextContents final : public Contents {
TextFrame frame_;
Color color_;
Scalar inherited_opacity_ = 1.0;
mutable std::shared_ptr<LazyGlyphAtlas> lazy_atlas_;
Vector2 offset_;
std::shared_ptr<GlyphAtlas> ResolveAtlas(
GlyphAtlas::Type type,
const std::shared_ptr<LazyGlyphAtlas>& lazy_atlas,
std::shared_ptr<GlyphAtlasContext> atlas_context,
std::shared_ptr<Context> context) const;

View File

@@ -166,8 +166,4 @@ bool Entity::Render(const ContentContext& renderer,
return contents_->Render(renderer, *this, parent_pass);
}
Scalar Entity::DeriveTextScale() const {
return GetTransformation().GetMaxBasisLengthXY();
}
} // namespace impeller

View File

@@ -94,8 +94,6 @@ class Entity {
std::optional<Color> AsBackgroundColor(ISize target_size) const;
Scalar DeriveTextScale() const;
private:
Matrix transformation_;
std::shared_ptr<Contents> contents_;

View File

@@ -8,7 +8,6 @@
#include <utility>
#include <variant>
#include "flutter/fml/closure.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/macros.h"
#include "flutter/fml/trace_event.h"
@@ -253,18 +252,6 @@ bool EntityPass::Render(ContentContext& renderer,
return false;
}
renderer.SetLazyGlyphAtlas(std::make_shared<LazyGlyphAtlas>());
fml::ScopedCleanupClosure reset_lazy_glyph_atlas(
[&renderer]() { renderer.SetLazyGlyphAtlas(nullptr); });
IterateAllEntities([lazy_glyph_atlas =
renderer.GetLazyGlyphAtlas()](const Entity& entity) {
if (auto contents = entity.GetContents()) {
contents->PopulateGlyphAtlas(lazy_glyph_atlas, entity.DeriveTextScale());
}
return true;
});
StencilCoverageStack stencil_coverage_stack = {StencilCoverageLayer{
.coverage = Rect::MakeSize(root_render_target.GetRenderTargetSize()),
.stencil_depth = 0}};
@@ -891,28 +878,6 @@ void EntityPass::IterateAllEntities(
}
}
void EntityPass::IterateAllEntities(
const std::function<bool(const Entity&)>& iterator) const {
if (!iterator) {
return;
}
for (const auto& element : elements_) {
if (auto entity = std::get_if<Entity>(&element)) {
if (!iterator(*entity)) {
return;
}
continue;
}
if (auto subpass = std::get_if<std::unique_ptr<EntityPass>>(&element)) {
const EntityPass* entity_pass = subpass->get();
entity_pass->IterateAllEntities(iterator);
continue;
}
FML_UNREACHABLE();
}
}
bool EntityPass::IterateUntilSubpass(
const std::function<bool(Entity&)>& iterator) {
if (!iterator) {

View File

@@ -79,12 +79,6 @@ class EntityPass {
/// of child passes. The iteration order is depth-first.
void IterateAllEntities(const std::function<bool(Entity&)>& iterator);
/// @brief Iterate all entities in this pass, recursively including entities
/// of child passes. The iteration order is depth-first and does not
/// allow modification of the entities.
void IterateAllEntities(
const std::function<bool(const Entity&)>& iterator) const;
/// @brief Iterate entities in this pass up until the first subpass is found.
/// This is useful for limiting look-ahead optimizations.
///

View File

@@ -2184,7 +2184,7 @@ TEST_P(EntityTest, InheritOpacityTest) {
auto blob = SkTextBlob::MakeFromString("A", font);
auto frame = TextFrameFromTextBlob(blob);
auto lazy_glyph_atlas = std::make_shared<LazyGlyphAtlas>();
lazy_glyph_atlas->AddTextFrame(frame, 1.0f);
lazy_glyph_atlas->AddTextFrame(frame);
auto text_contents = std::make_shared<TextContents>();
text_contents->SetTextFrame(frame);

View File

@@ -17,7 +17,7 @@
namespace impeller {
static Font ToFont(const SkTextBlobRunIterator& run) {
static Font ToFont(const SkTextBlobRunIterator& run, Scalar scale) {
auto& font = run.font();
auto typeface = std::make_shared<TypefaceSkia>(font.refTypefaceOrDefault());
@@ -25,6 +25,7 @@ static Font ToFont(const SkTextBlobRunIterator& run) {
font.getMetrics(&sk_metrics);
Font::Metrics metrics;
metrics.scale = scale;
metrics.point_size = font.getSize();
metrics.embolden = font.isEmbolden();
metrics.skewX = font.getSkewX();
@@ -37,7 +38,7 @@ static Rect ToRect(const SkRect& rect) {
return Rect::MakeLTRB(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
}
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob) {
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob, Scalar scale) {
if (!blob) {
return {};
}
@@ -45,7 +46,7 @@ TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob) {
TextFrame frame;
for (SkTextBlobRunIterator run(blob.get()); !run.done(); run.next()) {
TextRun text_run(ToFont(run));
TextRun text_run(ToFont(run, scale));
// TODO(jonahwilliams): ask Skia for a public API to look this up.
// https://github.com/flutter/flutter/issues/112005

View File

@@ -10,6 +10,7 @@
namespace impeller {
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob);
TextFrame TextFrameFromTextBlob(const sk_sp<SkTextBlob>& blob,
Scalar scale = 1.0f);
} // namespace impeller

View File

@@ -40,6 +40,23 @@ TextRenderContextSkia::TextRenderContextSkia(std::shared_ptr<Context> context)
TextRenderContextSkia::~TextRenderContextSkia() = default;
static FontGlyphPair::Set CollectUniqueFontGlyphPairs(
GlyphAtlas::Type type,
const TextRenderContext::FrameIterator& frame_iterator) {
TRACE_EVENT0("impeller", __FUNCTION__);
FontGlyphPair::Set set;
while (const TextFrame* frame = frame_iterator()) {
for (const TextRun& run : frame->GetRuns()) {
const Font& font = run.GetFont();
for (const TextRun::GlyphPosition& glyph_position :
run.GetGlyphPositions()) {
set.insert({font, glyph_position.glyph});
}
}
}
return set;
}
static size_t PairsFitInAtlasOfSize(
const FontGlyphPair::Set& pairs,
const ISize& atlas_size,
@@ -56,7 +73,8 @@ static size_t PairsFitInAtlasOfSize(
for (auto it = pairs.begin(); it != pairs.end(); ++i, ++it) {
const auto& pair = *it;
const auto glyph_size = ISize::Ceil((pair.glyph.bounds * pair.scale).size);
const auto glyph_size =
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
IPoint16 location_in_atlas;
if (!rect_packer->addRect(glyph_size.width + kPadding, //
glyph_size.height + kPadding, //
@@ -93,7 +111,8 @@ static bool CanAppendToExistingAtlas(
for (size_t i = 0; i < extra_pairs.size(); i++) {
const FontGlyphPair& pair = extra_pairs[i];
const auto glyph_size = ISize::Ceil((pair.glyph.bounds * pair.scale).size);
const auto glyph_size =
ISize::Ceil((pair.glyph.bounds * pair.font.GetMetrics().scale).size);
IPoint16 location_in_atlas;
if (!rect_packer->addRect(glyph_size.width + kPadding, //
glyph_size.height + kPadding, //
@@ -157,8 +176,8 @@ static void DrawGlyph(SkCanvas* canvas,
const Rect& location,
bool has_color) {
const auto& metrics = font_glyph.font.GetMetrics();
const auto position = SkPoint::Make(location.origin.x / font_glyph.scale,
location.origin.y / font_glyph.scale);
const auto position = SkPoint::Make(location.origin.x / metrics.scale,
location.origin.y / metrics.scale);
SkGlyphID glyph_id = font_glyph.glyph.index;
SkFont sk_font(
@@ -173,7 +192,7 @@ static void DrawGlyph(SkCanvas* canvas,
SkPaint glyph_paint;
glyph_paint.setColor(glyph_color);
canvas->resetMatrix();
canvas->scale(font_glyph.scale, font_glyph.scale);
canvas->scale(metrics.scale, metrics.scale);
canvas->drawGlyphs(
1u, // count
&glyph_id, // glyphs
@@ -312,19 +331,25 @@ static std::shared_ptr<Texture> UploadGlyphTextureAtlas(
std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const FontGlyphPair::Set& font_glyph_pairs) const {
FrameIterator frame_iterator) const {
TRACE_EVENT0("impeller", __FUNCTION__);
if (!IsValid()) {
return nullptr;
}
std::shared_ptr<GlyphAtlas> last_atlas = atlas_context->GetGlyphAtlas();
// ---------------------------------------------------------------------------
// Step 1: Collect unique font-glyph pairs in the frame.
// ---------------------------------------------------------------------------
FontGlyphPair::Set font_glyph_pairs =
CollectUniqueFontGlyphPairs(type, frame_iterator);
if (font_glyph_pairs.empty()) {
return last_atlas;
}
// ---------------------------------------------------------------------------
// Step 1: Determine if the atlas type and font glyph pairs are compatible
// Step 2: Determine if the atlas type and font glyph pairs are compatible
// with the current atlas and reuse if possible.
// ---------------------------------------------------------------------------
FontGlyphPairRefVector new_glyphs;
@@ -338,7 +363,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
}
// ---------------------------------------------------------------------------
// Step 2: Determine if the additional missing glyphs can be appended to the
// Step 3: Determine if the additional missing glyphs can be appended to the
// existing bitmap without recreating the atlas. This requires that
// the type is identical.
// ---------------------------------------------------------------------------
@@ -351,7 +376,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
// added.
// ---------------------------------------------------------------------------
// Step 3a: Record the positions in the glyph atlas of the newly added
// Step 4: Record the positions in the glyph atlas of the newly added
// glyphs.
// ---------------------------------------------------------------------------
for (size_t i = 0, count = glyph_positions.size(); i < count; i++) {
@@ -359,7 +384,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
}
// ---------------------------------------------------------------------------
// Step 4a: Draw new font-glyph pairs into the existing bitmap.
// Step 5: Draw new font-glyph pairs into the existing bitmap.
// ---------------------------------------------------------------------------
auto bitmap = atlas_context->GetBitmap();
if (!UpdateAtlasBitmap(*last_atlas, bitmap, new_glyphs)) {
@@ -367,7 +392,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
}
// ---------------------------------------------------------------------------
// Step 5a: Update the existing texture with the updated bitmap.
// Step 6: Update the existing texture with the updated bitmap.
// ---------------------------------------------------------------------------
if (!UpdateGlyphTextureAtlas(bitmap, last_atlas->GetTexture())) {
return nullptr;
@@ -377,7 +402,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
// A new glyph atlas must be created.
// ---------------------------------------------------------------------------
// Step 3b: Get the optimum size of the texture atlas.
// Step 4: Get the optimum size of the texture atlas.
// ---------------------------------------------------------------------------
auto glyph_atlas = std::make_shared<GlyphAtlas>(type);
auto atlas_size = OptimumAtlasSizeForFontGlyphPairs(
@@ -388,7 +413,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
return nullptr;
}
// ---------------------------------------------------------------------------
// Step 4b: Find location of font-glyph pairs in the atlas. We have this from
// Step 5: Find location of font-glyph pairs in the atlas. We have this from
// the last step. So no need to do create another rect packer. But just do a
// sanity check of counts. This could also be just an assertion as only a
// construction issue would cause such a failure.
@@ -398,7 +423,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
}
// ---------------------------------------------------------------------------
// Step 5b: Record the positions in the glyph atlas.
// Step 6: Record the positions in the glyph atlas.
// ---------------------------------------------------------------------------
{
size_t i = 0;
@@ -409,7 +434,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
}
// ---------------------------------------------------------------------------
// Step 6b: Draw font-glyph pairs in the correct spot in the atlas.
// Step 7: Draw font-glyph pairs in the correct spot in the atlas.
// ---------------------------------------------------------------------------
auto bitmap = CreateAtlasBitmap(*glyph_atlas, atlas_size);
if (!bitmap) {
@@ -418,7 +443,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
atlas_context->UpdateBitmap(bitmap);
// ---------------------------------------------------------------------------
// Step 7b: Upload the atlas as a texture.
// Step 8: Upload the atlas as a texture.
// ---------------------------------------------------------------------------
PixelFormat format;
switch (type) {
@@ -436,7 +461,7 @@ std::shared_ptr<GlyphAtlas> TextRenderContextSkia::CreateGlyphAtlas(
}
// ---------------------------------------------------------------------------
// Step 8b: Record the texture in the glyph atlas.
// Step 9: Record the texture in the glyph atlas.
// ---------------------------------------------------------------------------
glyph_atlas->SetTexture(std::move(texture));

View File

@@ -19,7 +19,7 @@ class TextRenderContextSkia : public TextRenderContext {
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const FontGlyphPair::Set& font_glyph_pairs) const override;
FrameIterator iterator) const override;
private:
FML_DISALLOW_COPY_AND_ASSIGN(TextRenderContextSkia);

View File

@@ -28,6 +28,12 @@ class Font : public Comparable<Font> {
/// the baseline with an upper-left-origin coordinate system.
///
struct Metrics {
//--------------------------------------------------------------------------
/// The scaling factor that should be used when rendering this font to an
/// atlas. This should normally be set in accordance with the transformation
/// matrix that will be used to position glyph geometry.
///
Scalar scale = 1.0f;
//--------------------------------------------------------------------------
/// The point size of the font.
///
@@ -37,8 +43,8 @@ class Font : public Comparable<Font> {
Scalar scaleX = 1.0f;
constexpr bool operator==(const Metrics& o) const {
return point_size == o.point_size && embolden == o.embolden &&
skewX == o.skewX && scaleX == o.scaleX;
return scale == o.scale && point_size == o.point_size &&
embolden == o.embolden && skewX == o.skewX && scaleX == o.scaleX;
}
};
@@ -74,6 +80,6 @@ class Font : public Comparable<Font> {
template <>
struct std::hash<impeller::Font::Metrics> {
constexpr std::size_t operator()(const impeller::Font::Metrics& m) const {
return fml::HashCombine(m.point_size, m.skewX, m.scaleX);
return fml::HashCombine(m.scale, m.point_size);
}
};

View File

@@ -16,29 +16,28 @@
namespace impeller {
//------------------------------------------------------------------------------
/// @brief A font along with a glyph in that font rendered at a particular
/// scale. Used in glyph atlases as keys.
/// @brief A font along with a glyph in that font. Used in glyph atlases as
/// keys.
///
struct FontGlyphPair {
struct Hash;
struct Equal;
using Set = std::unordered_set<FontGlyphPair, Hash, Equal>;
using Vector = std::vector<FontGlyphPair>;
Font font;
Glyph glyph;
Scalar scale;
struct Hash {
std::size_t operator()(const FontGlyphPair& p) const {
return fml::HashCombine(p.font.GetHash(), p.glyph.index, p.glyph.type,
p.scale);
return fml::HashCombine(p.font.GetHash(), p.glyph.index, p.glyph.type);
}
};
struct Equal {
bool operator()(const FontGlyphPair& lhs, const FontGlyphPair& rhs) const {
return lhs.font.IsEqual(rhs.font) && lhs.glyph.index == rhs.glyph.index &&
lhs.glyph.type == rhs.glyph.type && lhs.scale == rhs.scale;
lhs.glyph.type == rhs.glyph.type;
}
};
};

View File

@@ -15,12 +15,12 @@ LazyGlyphAtlas::LazyGlyphAtlas() = default;
LazyGlyphAtlas::~LazyGlyphAtlas() = default;
void LazyGlyphAtlas::AddTextFrame(const TextFrame& frame, Scalar scale) {
void LazyGlyphAtlas::AddTextFrame(const TextFrame& frame) {
FML_DCHECK(atlas_map_.empty());
if (frame.GetAtlasType() == GlyphAtlas::Type::kAlphaBitmap) {
frame.CollectUniqueFontGlyphPairs(alpha_set_, scale);
alpha_frames_.emplace_back(frame);
} else {
frame.CollectUniqueFontGlyphPairs(color_set_, scale);
color_frames_.emplace_back(frame);
}
}
@@ -39,9 +39,19 @@ std::shared_ptr<GlyphAtlas> LazyGlyphAtlas::CreateOrGetGlyphAtlas(
if (!text_context || !text_context->IsValid()) {
return nullptr;
}
auto& set = type == GlyphAtlas::Type::kAlphaBitmap ? alpha_set_ : color_set_;
size_t i = 0;
auto frames =
type == GlyphAtlas::Type::kAlphaBitmap ? alpha_frames_ : color_frames_;
TextRenderContext::FrameIterator iterator = [&]() -> const TextFrame* {
if (i >= frames.size()) {
return nullptr;
}
const auto& result = frames[i];
i++;
return &result;
};
auto atlas =
text_context->CreateGlyphAtlas(type, std::move(atlas_context), set);
text_context->CreateGlyphAtlas(type, std::move(atlas_context), iterator);
if (!atlas || !atlas->IsValid()) {
VALIDATION_LOG << "Could not create valid atlas.";
return nullptr;

View File

@@ -19,7 +19,7 @@ class LazyGlyphAtlas {
~LazyGlyphAtlas();
void AddTextFrame(const TextFrame& frame, Scalar scale);
void AddTextFrame(const TextFrame& frame);
std::shared_ptr<GlyphAtlas> CreateOrGetGlyphAtlas(
GlyphAtlas::Type type,
@@ -27,8 +27,8 @@ class LazyGlyphAtlas {
std::shared_ptr<Context> context) const;
private:
FontGlyphPair::Set alpha_set_;
FontGlyphPair::Set color_set_;
std::vector<TextFrame> alpha_frames_;
std::vector<TextFrame> color_frames_;
mutable std::unordered_map<GlyphAtlas::Type, std::shared_ptr<GlyphAtlas>>
atlas_map_;

View File

@@ -79,15 +79,4 @@ bool TextFrame::MaybeHasOverlapping() const {
return false;
}
void TextFrame::CollectUniqueFontGlyphPairs(FontGlyphPair::Set& set,
Scalar scale) const {
for (const TextRun& run : GetRuns()) {
const Font& font = run.GetFont();
for (const TextRun::GlyphPosition& glyph_position :
run.GetGlyphPositions()) {
set.insert({font, glyph_position.glyph, scale});
}
}
}
} // namespace impeller

View File

@@ -22,8 +22,6 @@ class TextFrame {
~TextFrame();
void CollectUniqueFontGlyphPairs(FontGlyphPair::Set& set, Scalar scale) const;
//----------------------------------------------------------------------------
/// @brief The conservative bounding box for this text frame.
///

View File

@@ -26,4 +26,19 @@ const std::shared_ptr<Context>& TextRenderContext::GetContext() const {
return context_;
}
std::shared_ptr<GlyphAtlas> TextRenderContext::CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const TextFrame& frame) const {
size_t count = 0;
FrameIterator iterator = [&]() -> const TextFrame* {
count++;
if (count == 1) {
return &frame;
}
return nullptr;
};
return CreateGlyphAtlas(type, std::move(atlas_context), iterator);
}
} // namespace impeller

View File

@@ -37,13 +37,20 @@ class TextRenderContext {
///
const std::shared_ptr<Context>& GetContext() const;
using FrameIterator = std::function<const TextFrame*(void)>;
// TODO(dnfield): Callers should not need to know which type of atlas to
// create. https://github.com/flutter/flutter/issues/111640
virtual std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const FontGlyphPair::Set& font_glyph_pairs) const = 0;
FrameIterator iterator) const = 0;
std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
GlyphAtlas::Type type,
std::shared_ptr<GlyphAtlasContext> atlas_context,
const TextFrame& frame) const;
protected:
//----------------------------------------------------------------------------

View File

@@ -23,17 +23,6 @@ namespace testing {
using TypographerTest = PlaygroundTest;
INSTANTIATE_PLAYGROUND_SUITE(TypographerTest);
static std::shared_ptr<GlyphAtlas> CreateGlyphAtlas(
const TextRenderContext* context,
GlyphAtlas::Type type,
Scalar scale,
const std::shared_ptr<GlyphAtlasContext>& atlas_context,
const TextFrame& frame) {
FontGlyphPair::Set set;
frame.CollectUniqueFontGlyphPairs(set, scale);
return context->CreateGlyphAtlas(type, atlas_context, set);
}
TEST_P(TypographerTest, CanConvertTextBlob) {
SkFont font;
auto blob = SkTextBlob::MakeFromString(
@@ -60,8 +49,8 @@ TEST_P(TypographerTest, CanCreateGlyphAtlas) {
auto blob = SkTextBlob::MakeFromString("hello", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
ASSERT_EQ(atlas->GetType(), GlyphAtlas::Type::kAlphaBitmap);
@@ -113,13 +102,13 @@ TEST_P(TypographerTest, LazyAtlasTracksColor) {
LazyGlyphAtlas lazy_atlas;
lazy_atlas.AddTextFrame(frame, 1.0f);
lazy_atlas.AddTextFrame(frame);
frame = TextFrameFromTextBlob(SkTextBlob::MakeFromString("😀 ", emoji_font));
ASSERT_TRUE(frame.GetAtlasType() == GlyphAtlas::Type::kColorBitmap);
lazy_atlas.AddTextFrame(frame, 1.0f);
lazy_atlas.AddTextFrame(frame);
// Creates different atlases for color and alpha bitmap.
auto color_context = std::make_shared<GlyphAtlasContext>();
@@ -141,8 +130,8 @@ TEST_P(TypographerTest, GlyphAtlasWithOddUniqueGlyphSize) {
auto blob = SkTextBlob::MakeFromString("AGH", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
@@ -158,8 +147,8 @@ TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
auto blob = SkTextBlob::MakeFromString("spooky skellingtons", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
ASSERT_EQ(atlas, atlas_context->GetGlyphAtlas());
@@ -167,8 +156,8 @@ TEST_P(TypographerTest, GlyphAtlasIsRecycledIfUnchanged) {
// now attempt to re-create an atlas with the same text blob.
auto next_atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
ASSERT_EQ(atlas, next_atlas);
ASSERT_EQ(atlas_context->GetGlyphAtlas(), atlas);
}
@@ -177,6 +166,7 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
auto context = TextRenderContext::Create(GetContext());
auto atlas_context = std::make_shared<GlyphAtlasContext>();
ASSERT_TRUE(context && context->IsValid());
SkFont sk_font;
const char* test_string =
"QWERTYUIOPASDFGHJKLZXCVBNMqewrtyuiopasdfghjklzxcvbnm,.<>[]{};':"
@@ -184,17 +174,22 @@ TEST_P(TypographerTest, GlyphAtlasWithLotsOfdUniqueGlyphSize) {
"œ∑´®†¥¨ˆøπ““‘‘åß∂ƒ©˙∆˚¬…æ≈ç√∫˜µ≤≥≥≥≥÷¡™£¢∞§¶•ªº–≠⁄€‹›fifl‡°·‚—±Œ„´‰Á¨Ø∏”’/"
"* Í˝ */¸˛Ç◊ı˜Â¯˘¿";
SkFont sk_font;
auto blob = SkTextBlob::MakeFromString(test_string, sk_font);
ASSERT_TRUE(blob);
FontGlyphPair::Set set;
size_t size_count = 8;
for (size_t index = 0; index < size_count; index += 1) {
TextFrameFromTextBlob(blob).CollectUniqueFontGlyphPairs(set, 0.6 * index);
TextFrame frame;
size_t count = 0;
const int size_count = 8;
TextRenderContext::FrameIterator iterator = [&]() -> const TextFrame* {
if (count < size_count) {
count++;
frame = TextFrameFromTextBlob(blob, 0.6 * count);
return &frame;
}
return nullptr;
};
auto atlas = context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap,
std::move(atlas_context), set);
atlas_context, iterator);
ASSERT_NE(atlas, nullptr);
ASSERT_NE(atlas->GetTexture(), nullptr);
@@ -222,8 +217,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
auto old_packer = atlas_context->GetRectPacker();
ASSERT_NE(atlas, nullptr);
@@ -236,8 +231,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecycledIfUnchanged) {
auto blob2 = SkTextBlob::MakeFromString("spooky 2", sk_font);
auto next_atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob2));
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob2));
ASSERT_EQ(atlas, next_atlas);
auto* second_texture = next_atlas->GetTexture().get();
@@ -255,8 +250,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
auto blob = SkTextBlob::MakeFromString("spooky 1", sk_font);
ASSERT_TRUE(blob);
auto atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kAlphaBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob));
context->CreateGlyphAtlas(GlyphAtlas::Type::kAlphaBitmap, atlas_context,
TextFrameFromTextBlob(blob));
auto old_packer = atlas_context->GetRectPacker();
ASSERT_NE(atlas, nullptr);
@@ -270,8 +265,8 @@ TEST_P(TypographerTest, GlyphAtlasTextureIsRecreatedIfTypeChanges) {
auto blob2 = SkTextBlob::MakeFromString("spooky 1", sk_font);
auto next_atlas =
CreateGlyphAtlas(context.get(), GlyphAtlas::Type::kColorBitmap, 1.0f,
atlas_context, TextFrameFromTextBlob(blob2));
context->CreateGlyphAtlas(GlyphAtlas::Type::kColorBitmap, atlas_context,
TextFrameFromTextBlob(blob2));
ASSERT_NE(atlas, next_atlas);
auto* second_texture = next_atlas->GetTexture().get();