diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index f3cf26206c773..7c510864e4266 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -1121,6 +1121,8 @@ ORIGIN: ../../../flutter/impeller/entity/contents/clip_contents.cc + ../../../fl ORIGIN: ../../../flutter/impeller/entity/contents/clip_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/color_source_contents.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/color_source_contents.h + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/color_source_text_contents.cc + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/impeller/entity/contents/color_source_text_contents.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/content_context.cc + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/content_context.h + ../../../flutter/LICENSE ORIGIN: ../../../flutter/impeller/entity/contents/contents.cc + ../../../flutter/LICENSE @@ -3655,6 +3657,8 @@ FILE: ../../../flutter/impeller/entity/contents/clip_contents.cc FILE: ../../../flutter/impeller/entity/contents/clip_contents.h FILE: ../../../flutter/impeller/entity/contents/color_source_contents.cc FILE: ../../../flutter/impeller/entity/contents/color_source_contents.h +FILE: ../../../flutter/impeller/entity/contents/color_source_text_contents.cc +FILE: ../../../flutter/impeller/entity/contents/color_source_text_contents.h FILE: ../../../flutter/impeller/entity/contents/content_context.cc FILE: ../../../flutter/impeller/entity/contents/content_context.h FILE: ../../../flutter/impeller/entity/contents/contents.cc diff --git a/impeller/aiks/canvas.cc b/impeller/aiks/canvas.cc index 0b86986c34a3e..b88543c1596da 100644 --- a/impeller/aiks/canvas.cc +++ b/impeller/aiks/canvas.cc @@ -12,6 +12,7 @@ #include "impeller/aiks/paint_pass_delegate.h" #include "impeller/entity/contents/atlas_contents.h" #include "impeller/entity/contents/clip_contents.h" +#include "impeller/entity/contents/color_source_text_contents.h" #include "impeller/entity/contents/rrect_shadow_contents.h" #include "impeller/entity/contents/text_contents.h" #include "impeller/entity/contents/texture_contents.h" @@ -380,16 +381,40 @@ void Canvas::DrawTextFrame(const TextFrame& text_frame, 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(); text_contents->SetTextFrame(text_frame); text_contents->SetGlyphAtlas(lazy_glyph_atlas_); + + if (paint.color_source.has_value()) { + auto& source = paint.color_source.value(); + auto color_text_contents = std::make_shared(); + entity.SetTransformation(GetCurrentTransformation()); + + Entity test; + auto cvg = text_contents->GetCoverage(test).value(); + color_text_contents->SetTextPosition(cvg.origin + position); + + text_contents->SetInverseMatrix( + Matrix::MakeTranslation(Vector3(-cvg.origin.x, -cvg.origin.y, 0))); + color_text_contents->SetTextContents(std::move(text_contents)); + color_text_contents->SetColorSourceContents(source()); + + entity.SetContents( + paint.WithFilters(std::move(color_text_contents), false)); + + GetCurrentPass().AddEntity(entity); + return; + } + text_contents->SetColor(paint.color); - Entity entity; entity.SetTransformation(GetCurrentTransformation() * Matrix::MakeTranslation(position)); - entity.SetStencilDepth(GetStencilDepth()); - entity.SetBlendMode(paint.blend_mode); + entity.SetContents(paint.WithFilters(std::move(text_contents), true)); GetCurrentPass().AddEntity(entity); diff --git a/impeller/display_list/display_list_unittests.cc b/impeller/display_list/display_list_unittests.cc index 0c5c9565b69f3..d214406eca2d2 100644 --- a/impeller/display_list/display_list_unittests.cc +++ b/impeller/display_list/display_list_unittests.cc @@ -57,6 +57,25 @@ TEST_P(DisplayListTest, CanDrawTextBlob) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_P(DisplayListTest, CanDrawTextBlobWithGradient) { + flutter::DisplayListBuilder builder; + + std::vector colors = {flutter::DlColor::kBlue(), + flutter::DlColor::kRed()}; + const float stops[2] = {0.0, 1.0}; + + auto linear = flutter::DlColorSource::MakeLinear({0.0, 0.0}, {300.0, 300.0}, + 2, colors.data(), stops, + flutter::DlTileMode::kClamp); + flutter::DlPaint paint; + paint.setColorSource(linear); + + builder.DrawTextBlob( + SkTextBlob::MakeFromString("Hello World", CreateTestFont()), 100, 100, + paint); + ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); +} + TEST_P(DisplayListTest, CanDrawTextWithSaveLayer) { flutter::DisplayListBuilder builder; builder.setColor(SK_ColorRED); diff --git a/impeller/entity/BUILD.gn b/impeller/entity/BUILD.gn index 1c1fc90d25776..0a68451519643 100644 --- a/impeller/entity/BUILD.gn +++ b/impeller/entity/BUILD.gn @@ -120,6 +120,8 @@ impeller_component("entity") { "contents/clip_contents.h", "contents/color_source_contents.cc", "contents/color_source_contents.h", + "contents/color_source_text_contents.cc", + "contents/color_source_text_contents.h", "contents/content_context.cc", "contents/content_context.h", "contents/contents.cc", diff --git a/impeller/entity/contents/color_source_contents.h b/impeller/entity/contents/color_source_contents.h index 89a86cf47ea78..5eacfc6041a39 100644 --- a/impeller/entity/contents/color_source_contents.h +++ b/impeller/entity/contents/color_source_contents.h @@ -22,6 +22,8 @@ class ColorSourceContents : public Contents { void SetEffectTransform(Matrix matrix); + const Matrix& GetInverseMatrix() const; + void SetAlpha(Scalar alpha); // |Contents| @@ -34,8 +36,6 @@ class ColorSourceContents : public Contents { protected: const std::shared_ptr& GetGeometry() const; - const Matrix& GetInverseMatrix() const; - Scalar GetAlpha() const; private: diff --git a/impeller/entity/contents/color_source_text_contents.cc b/impeller/entity/contents/color_source_text_contents.cc new file mode 100644 index 0000000000000..3301fb26c9e7e --- /dev/null +++ b/impeller/entity/contents/color_source_text_contents.cc @@ -0,0 +1,84 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "impeller/entity/contents/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" + +namespace impeller { + +ColorSourceTextContents::ColorSourceTextContents() = default; + +ColorSourceTextContents::~ColorSourceTextContents() = default; + +void ColorSourceTextContents::SetTextContents( + std::shared_ptr text_contents) { + text_contents_ = std::move(text_contents); +} + +void ColorSourceTextContents::SetColorSourceContents( + std::shared_ptr color_source_contents) { + color_source_contents_ = std::move(color_source_contents); +} + +std::optional ColorSourceTextContents::GetCoverage( + const Entity& entity) const { + return text_contents_->GetCoverage(entity); +} + +void ColorSourceTextContents::SetTextPosition(Point position) { + position_ = position; +} + +bool ColorSourceTextContents::Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const { + auto coverage = text_contents_->GetCoverage(entity); + if (!coverage.has_value()) { + return true; + } + auto transform = entity.GetTransformation(); + + text_contents_->SetColor(Color::Black()); + color_source_contents_->SetGeometry( + Geometry::MakeRect(Rect::MakeSize(coverage->size))); + + // offset the color source so it behaves as if it were drawn in the original + // position. + auto effect_transform = + color_source_contents_->GetInverseMatrix().Invert().Translate(-position_); + color_source_contents_->SetEffectTransform(effect_transform); + + auto new_texture = renderer.MakeSubpass( + "Text Color Blending", ISize::Ceil(coverage.value().size), + [&](const ContentContext& context, RenderPass& pass) { + Entity sub_entity; + sub_entity.SetTransformation(transform); + sub_entity.SetContents(text_contents_); + sub_entity.SetBlendMode(BlendMode::kSource); + if (!sub_entity.Render(context, pass)) { + return false; + } + + sub_entity.SetContents(color_source_contents_); + sub_entity.SetBlendMode(BlendMode::kSourceIn); + return sub_entity.Render(context, pass); + }); + if (!new_texture) { + return false; + } + + auto dest_rect = Rect::MakeSize(new_texture->GetSize()) + .TransformBounds(transform.Invert()) + .Shift(position_); + + auto texture_contents = TextureContents::MakeRect(dest_rect); + texture_contents->SetTexture(new_texture); + texture_contents->SetSourceRect(Rect::MakeSize(new_texture->GetSize())); + return texture_contents->Render(renderer, entity, pass); +} + +} // namespace impeller diff --git a/impeller/entity/contents/color_source_text_contents.h b/impeller/entity/contents/color_source_text_contents.h new file mode 100644 index 0000000000000..e93738c8f3760 --- /dev/null +++ b/impeller/entity/contents/color_source_text_contents.h @@ -0,0 +1,48 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#pragma once + +#include +#include +#include +#include + +#include "flutter/fml/macros.h" +#include "impeller/entity/contents/color_source_contents.h" +#include "impeller/entity/contents/contents.h" +#include "impeller/entity/contents/text_contents.h" + +namespace impeller { + +class ColorSourceTextContents final : public Contents { + public: + ColorSourceTextContents(); + + ~ColorSourceTextContents(); + + void SetTextContents(std::shared_ptr text_contents); + + void SetColorSourceContents( + std::shared_ptr color_source_contents); + + void SetTextPosition(Point position); + + // |Contents| + std::optional GetCoverage(const Entity& entity) const override; + + // |Contents| + bool Render(const ContentContext& renderer, + const Entity& entity, + RenderPass& pass) const override; + + private: + Point position_; + std::shared_ptr text_contents_; + std::shared_ptr color_source_contents_; + + FML_DISALLOW_COPY_AND_ASSIGN(ColorSourceTextContents); +}; + +} // namespace impeller diff --git a/impeller/entity/contents/text_contents.cc b/impeller/entity/contents/text_contents.cc index 141ffaccea293..713dbbaf2033b 100644 --- a/impeller/entity/contents/text_contents.cc +++ b/impeller/entity/contents/text_contents.cc @@ -50,6 +50,10 @@ void TextContents::SetColor(Color color) { color_ = color; } +void TextContents::SetInverseMatrix(Matrix matrix) { + inverse_matrix_ = matrix; +} + std::optional TextContents::GetCoverage(const Entity& entity) const { auto bounds = frame_.GetBounds(); if (!bounds.has_value()) { @@ -71,6 +75,7 @@ static bool CommonRender( RenderPass& pass, const Color& color, const TextFrame& frame, + const Matrix& inverse_matrix, std::shared_ptr atlas, // NOLINT(performance-unnecessary-value-param) Command& cmd) { @@ -159,8 +164,10 @@ static bool CommonRender( auto uv_scaler_a = atlas_glyph_pos->size / atlas_size; auto uv_scaler_b = (Point::Round(atlas_glyph_pos->origin) / atlas_size); - auto translation = Matrix::MakeTranslation( - Vector3(offset_glyph_position.x, offset_glyph_position.y, 0)); + auto translation = + Matrix::MakeTranslation( + Vector3(offset_glyph_position.x, offset_glyph_position.y, 0)) * + inverse_matrix; for (const auto& point : unit_points) { typename VS::PerVertexData vtx; @@ -209,8 +216,8 @@ bool TextContents::RenderSdf(const ContentContext& renderer, cmd.pipeline = renderer.GetGlyphAtlasSdfPipeline(opts); cmd.stencil_reference = entity.GetStencilDepth(); - return CommonRender(renderer, entity, pass, color_, - frame_, atlas, cmd); + return CommonRender( + renderer, entity, pass, color_, frame_, inverse_matrix_, atlas, cmd); } bool TextContents::Render(const ContentContext& renderer, @@ -245,7 +252,7 @@ bool TextContents::Render(const ContentContext& renderer, cmd.stencil_reference = entity.GetStencilDepth(); return CommonRender(renderer, entity, pass, color_, - frame_, atlas, cmd); + frame_, inverse_matrix_, atlas, cmd); } } // namespace impeller diff --git a/impeller/entity/contents/text_contents.h b/impeller/entity/contents/text_contents.h index a0cec70a95103..0a6b5bf716e4b 100644 --- a/impeller/entity/contents/text_contents.h +++ b/impeller/entity/contents/text_contents.h @@ -32,6 +32,8 @@ class TextContents final : public Contents { void SetColor(Color color); + void SetInverseMatrix(Matrix matrix); + // |Contents| std::optional GetCoverage(const Entity& entity) const override; @@ -49,6 +51,7 @@ class TextContents final : public Contents { TextFrame frame_; Color color_; mutable std::shared_ptr lazy_atlas_; + Matrix inverse_matrix_; std::shared_ptr ResolveAtlas( GlyphAtlas::Type type, diff --git a/impeller/geometry/geometry_unittests.cc b/impeller/geometry/geometry_unittests.cc index 927f8536a3f49..b5720d0c0cebd 100644 --- a/impeller/geometry/geometry_unittests.cc +++ b/impeller/geometry/geometry_unittests.cc @@ -1646,6 +1646,13 @@ TEST(GeometryTest, RectGetPoints) { ASSERT_POINT_NEAR(points[3], Point(400, 600)); } +TEST(GeometryTest, RectShift) { + auto r = Rect::MakeLTRB(0, 0, 100, 100); + + ASSERT_EQ(r.Shift(Point(10, 5)), Rect::MakeLTRB(10, 5, 110, 105)); + ASSERT_EQ(r.Shift(Point(-10, -5)), Rect::MakeLTRB(-10, -5, 90, 95)); +} + TEST(GeometryTest, RectGetTransformedPoints) { Rect r(100, 200, 300, 400); auto points = r.GetTransformedPoints(Matrix::MakeTranslation({10, 20})); diff --git a/impeller/geometry/rect.h b/impeller/geometry/rect.h index 4dd4107107144..afeb68620d9a2 100644 --- a/impeller/geometry/rect.h +++ b/impeller/geometry/rect.h @@ -238,6 +238,12 @@ struct TRect { return *this; } + + /// @brief Returns a new rectangle translated by the given offset. + constexpr TRect Shift(TPoint offset) const { + return TRect(origin.x + offset.x, origin.y + offset.y, size.width, + size.height); + } }; using Rect = TRect;