Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit b9b1b63

Browse files
authored
Use full 4x4 matrix transforms in TransformLayer (#43536)
Fixes: flutter/flutter#82961 Fixes: flutter/flutter#113346 The fix was a simple fallout from the previous work to add support for SkM44 throughout the DL and Diff mechanisms (see flutter/flutter#82955, flutter/flutter#116198, #37394) Tested with its own existing and new unit tests as well as the test case from flutter/flutter#113346
1 parent 95316fb commit b9b1b63

11 files changed

+134
-11
lines changed

display_list/utils/dl_matrix_clip_tracker.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class Data3x3 : public DisplayListMatrixClipTracker::Data {
9292
SkMatrix matrix_;
9393
};
9494

95-
static bool is_3x3(const SkM44& m) {
95+
bool DisplayListMatrixClipTracker::is_3x3(const SkM44& m) {
9696
// clang-format off
9797
return ( m.rc(0, 2) == 0 &&
9898
m.rc(1, 2) == 0 &&

display_list/utils/dl_matrix_clip_tracker.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class DisplayListMatrixClipTracker {
2727
DisplayListMatrixClipTracker(const SkRect& cull_rect, const SkMatrix& matrix);
2828
DisplayListMatrixClipTracker(const SkRect& cull_rect, const SkM44& matrix);
2929

30+
static bool is_3x3(const SkM44& m44);
31+
3032
SkRect base_device_cull_rect() const { return saved_[0]->device_cull_rect(); }
3133

3234
bool using_4x4_matrix() const { return current_->is_4x4(); }

flow/diff_context.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ void DiffContext::PushTransform(const SkMatrix& transform) {
5757
clip_tracker_.transform(transform);
5858
}
5959

60+
void DiffContext::PushTransform(const SkM44& transform) {
61+
clip_tracker_.transform(transform);
62+
}
63+
6064
void DiffContext::MakeCurrentTransformIntegral() {
6165
// TODO(knopp): This is duplicated from LayerStack. Maybe should be part of
6266
// clip tracker?

flow/diff_context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "display_list/utils/dl_matrix_clip_tracker.h"
1313
#include "flutter/flow/paint_region.h"
1414
#include "flutter/fml/macros.h"
15+
#include "third_party/skia/include/core/SkM44.h"
1516
#include "third_party/skia/include/core/SkMatrix.h"
1617
#include "third_party/skia/include/core/SkRect.h"
1718

@@ -70,6 +71,7 @@ class DiffContext {
7071

7172
// Pushes additional transform for current subtree
7273
void PushTransform(const SkMatrix& transform);
74+
void PushTransform(const SkM44& transform);
7375

7476
// Pushes cull rect for current subtree
7577
bool PushCullRect(const SkRect& clip);

flow/layers/layer_state_stack.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -577,9 +577,13 @@ void MutatorContext::transform(const SkMatrix& matrix) {
577577
}
578578

579579
void MutatorContext::transform(const SkM44& m44) {
580-
layer_state_stack_->maybe_save_layer_for_transform(save_needed_);
581-
save_needed_ = false;
582-
layer_state_stack_->push_transform(m44);
580+
if (DisplayListMatrixClipTracker::is_3x3(m44)) {
581+
transform(m44.asM33());
582+
} else {
583+
layer_state_stack_->maybe_save_layer_for_transform(save_needed_);
584+
save_needed_ = false;
585+
layer_state_stack_->push_transform(m44);
586+
}
583587
}
584588

585589
void MutatorContext::integralTransform() {

flow/layers/transform_layer.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@
88

99
namespace flutter {
1010

11-
TransformLayer::TransformLayer(const SkMatrix& transform)
12-
: transform_(transform) {
13-
// Checks (in some degree) that SkMatrix transform_ is valid and initialized.
11+
TransformLayer::TransformLayer(const SkM44& transform) : transform_(transform) {
12+
// Checks (in some degree) that SkM44 transform_ is valid and initialized.
1413
//
1514
// If transform_ is uninitialized, this assert may look flaky as it doesn't
1615
// fail all the time, and some rerun may make it pass. But don't ignore it and
@@ -47,7 +46,7 @@ void TransformLayer::Preroll(PrerollContext* context) {
4746
SkRect child_paint_bounds = SkRect::MakeEmpty();
4847
PrerollChildren(context, &child_paint_bounds);
4948

50-
transform_.mapRect(&child_paint_bounds);
49+
transform_.asM33().mapRect(&child_paint_bounds);
5150
set_paint_bounds(child_paint_bounds);
5251
}
5352

flow/layers/transform_layer.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ namespace flutter {
1313
// at all. Hence |set_transform| must be called with an initialized SkMatrix.
1414
class TransformLayer : public ContainerLayer {
1515
public:
16-
explicit TransformLayer(const SkMatrix& transform);
16+
explicit TransformLayer(const SkMatrix& transform)
17+
: TransformLayer(SkM44(transform)) {}
18+
explicit TransformLayer(const SkM44& transform);
1719

1820
void Diff(DiffContext* context, const Layer* old_layer) override;
1921

@@ -22,7 +24,7 @@ class TransformLayer : public ContainerLayer {
2224
void Paint(PaintContext& context) const override;
2325

2426
private:
25-
SkMatrix transform_;
27+
SkM44 transform_;
2628

2729
FML_DISALLOW_COPY_AND_ASSIGN(TransformLayer);
2830
};

flow/layers/transform_layer_unittests.cc

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ TEST_F(TransformLayerTest, Identity) {
5656
EXPECT_TRUE(layer->needs_painting(paint_context()));
5757
EXPECT_EQ(mock_layer->parent_matrix(), SkMatrix()); // identity
5858
EXPECT_EQ(mock_layer->parent_cull_rect(), cull_rect);
59+
EXPECT_EQ(mock_layer->parent_mutators().stack_count(), 0u);
5960
EXPECT_EQ(mock_layer->parent_mutators(), MutatorsStack());
6061

6162
layer->Paint(display_list_paint_context());
@@ -118,6 +119,102 @@ TEST_F(TransformLayerTest, Simple) {
118119
EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
119120
}
120121

122+
TEST_F(TransformLayerTest, SimpleM44) {
123+
SkPath child_path;
124+
child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
125+
SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f);
126+
SkRect local_cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f);
127+
SkRect device_cull_rect = initial_transform.mapRect(local_cull_rect);
128+
SkMatrix layer_transform = SkMatrix::Translate(2.5f, 3.5f);
129+
SkMatrix inverse_layer_transform;
130+
EXPECT_TRUE(layer_transform.invert(&inverse_layer_transform));
131+
132+
auto mock_layer = std::make_shared<MockLayer>(child_path, DlPaint());
133+
auto layer = std::make_shared<TransformLayer>(SkM44::Translate(2.5f, 3.5f));
134+
layer->Add(mock_layer);
135+
136+
preroll_context()->state_stack.set_preroll_delegate(device_cull_rect,
137+
initial_transform);
138+
layer->Preroll(preroll_context());
139+
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
140+
EXPECT_EQ(layer->paint_bounds(),
141+
layer_transform.mapRect(mock_layer->paint_bounds()));
142+
EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds());
143+
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
144+
EXPECT_TRUE(layer->needs_painting(paint_context()));
145+
EXPECT_EQ(mock_layer->parent_matrix(),
146+
SkMatrix::Concat(initial_transform, layer_transform));
147+
EXPECT_EQ(mock_layer->parent_cull_rect(),
148+
inverse_layer_transform.mapRect(local_cull_rect));
149+
EXPECT_EQ(mock_layer->parent_mutators(),
150+
std::vector({Mutator(layer_transform)}));
151+
152+
layer->Paint(display_list_paint_context());
153+
DisplayListBuilder expected_builder;
154+
/* (Transform)layer::Paint */ {
155+
expected_builder.Save();
156+
{
157+
expected_builder.Transform(layer_transform);
158+
/* mock_layer::Paint */ {
159+
expected_builder.DrawPath(child_path, DlPaint());
160+
}
161+
}
162+
expected_builder.Restore();
163+
}
164+
EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
165+
}
166+
167+
TEST_F(TransformLayerTest, ComplexM44) {
168+
SkPath child_path;
169+
child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);
170+
SkMatrix initial_transform = SkMatrix::Translate(-0.5f, -0.5f);
171+
SkRect local_cull_rect = SkRect::MakeXYWH(2.0f, 2.0f, 14.0f, 14.0f);
172+
SkRect device_cull_rect = initial_transform.mapRect(local_cull_rect);
173+
SkM44 layer_transform44 = SkM44();
174+
layer_transform44.preTranslate(2.5f, 2.5f);
175+
// 20 degrees around the X axis
176+
layer_transform44.preConcat(SkM44::Rotate({1.0f, 0.0f, 0.0f}, M_PI / 9.0f));
177+
// 10 degrees around the Y axis
178+
layer_transform44.preConcat(SkM44::Rotate({0.0f, 1.0f, 0.0f}, M_PI / 18.0f));
179+
SkMatrix layer_transform = layer_transform44.asM33();
180+
SkMatrix inverse_layer_transform;
181+
EXPECT_TRUE(layer_transform.invert(&inverse_layer_transform));
182+
183+
auto mock_layer = std::make_shared<MockLayer>(child_path, DlPaint());
184+
auto layer = std::make_shared<TransformLayer>(layer_transform44);
185+
layer->Add(mock_layer);
186+
187+
preroll_context()->state_stack.set_preroll_delegate(device_cull_rect,
188+
initial_transform);
189+
layer->Preroll(preroll_context());
190+
EXPECT_EQ(mock_layer->paint_bounds(), child_path.getBounds());
191+
EXPECT_EQ(layer->paint_bounds(),
192+
layer_transform.mapRect(mock_layer->paint_bounds()));
193+
EXPECT_EQ(layer->child_paint_bounds(), mock_layer->paint_bounds());
194+
EXPECT_TRUE(mock_layer->needs_painting(paint_context()));
195+
EXPECT_TRUE(layer->needs_painting(paint_context()));
196+
EXPECT_EQ(mock_layer->parent_matrix(),
197+
SkMatrix::Concat(initial_transform, layer_transform));
198+
EXPECT_EQ(mock_layer->parent_cull_rect(),
199+
inverse_layer_transform.mapRect(local_cull_rect));
200+
EXPECT_EQ(mock_layer->parent_mutators(),
201+
std::vector({Mutator(layer_transform)}));
202+
203+
layer->Paint(display_list_paint_context());
204+
DisplayListBuilder expected_builder;
205+
/* (Transform)layer::Paint */ {
206+
expected_builder.Save();
207+
{
208+
expected_builder.Transform(layer_transform44);
209+
/* mock_layer::Paint */ {
210+
expected_builder.DrawPath(child_path, DlPaint());
211+
}
212+
}
213+
expected_builder.Restore();
214+
}
215+
EXPECT_TRUE(DisplayListsEQ_Verbose(display_list(), expected_builder.Build()));
216+
}
217+
121218
TEST_F(TransformLayerTest, Nested) {
122219
SkPath child_path;
123220
child_path.addRect(5.0f, 6.0f, 20.5f, 21.5f);

lib/ui/compositing/scene_builder.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ SceneBuilder::~SceneBuilder() = default;
4444
void SceneBuilder::pushTransform(Dart_Handle layer_handle,
4545
tonic::Float64List& matrix4,
4646
const fml::RefPtr<EngineLayer>& oldLayer) {
47-
SkMatrix sk_matrix = ToSkMatrix(matrix4);
47+
SkM44 sk_matrix = ToSkM44(matrix4);
4848
auto layer = std::make_shared<flutter::TransformLayer>(sk_matrix);
4949
PushLayer(layer);
5050
// matrix4 has to be released before we can return another Dart object

lib/ui/painting/matrix.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,17 @@ static const int kSkMatrixIndexToMatrix4Index[] = {
1818
// clang-format on
1919
};
2020

21+
SkM44 ToSkM44(const tonic::Float64List& matrix4) {
22+
// clang-format off
23+
return SkM44(
24+
SafeNarrow(matrix4[ 0]), SafeNarrow(matrix4[ 4]), SafeNarrow(matrix4[ 8]), SafeNarrow(matrix4[12]),
25+
SafeNarrow(matrix4[ 1]), SafeNarrow(matrix4[ 5]), SafeNarrow(matrix4[ 9]), SafeNarrow(matrix4[13]),
26+
SafeNarrow(matrix4[ 2]), SafeNarrow(matrix4[ 6]), SafeNarrow(matrix4[10]), SafeNarrow(matrix4[14]),
27+
SafeNarrow(matrix4[ 3]), SafeNarrow(matrix4[ 7]), SafeNarrow(matrix4[11]), SafeNarrow(matrix4[15])
28+
);
29+
// clang-format on
30+
}
31+
2132
SkMatrix ToSkMatrix(const tonic::Float64List& matrix4) {
2233
FML_DCHECK(matrix4.data());
2334
SkMatrix sk_matrix;

lib/ui/painting/matrix.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@
55
#ifndef FLUTTER_LIB_UI_PAINTING_MATRIX_H_
66
#define FLUTTER_LIB_UI_PAINTING_MATRIX_H_
77

8+
#include "third_party/skia/include/core/SkM44.h"
89
#include "third_party/skia/include/core/SkMatrix.h"
910
#include "third_party/tonic/typed_data/typed_list.h"
1011

1112
namespace flutter {
1213

14+
SkM44 ToSkM44(const tonic::Float64List& matrix4);
1315
SkMatrix ToSkMatrix(const tonic::Float64List& matrix4);
1416
tonic::Float64List ToMatrix4(const SkMatrix& sk_matrix);
1517

0 commit comments

Comments
 (0)