Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
ae12bf6b
Unverified
Commit
ae12bf6b
authored
May 25, 2021
by
Jim Graham
Committed by
GitHub
May 25, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add a bitmap operation property to transform widgets to enable/control bitmap transforms (#76742)
parent
132a746a
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
193 additions
and
21 deletions
+193
-21
filtered_child_animation.dart
...rks/macrobenchmarks/lib/src/filtered_child_animation.dart
+1
-0
proxy_box.dart
packages/flutter/lib/src/rendering/proxy_box.dart
+45
-11
basic.dart
packages/flutter/lib/src/widgets/basic.dart
+16
-1
transitions.dart
packages/flutter/lib/src/widgets/transitions.dart
+18
-9
transform_test.dart
packages/flutter/test/widgets/transform_test.dart
+113
-0
No files found.
dev/benchmarks/macrobenchmarks/lib/src/filtered_child_animation.dart
View file @
ae12bf6b
...
...
@@ -120,6 +120,7 @@ class _FilteredChildAnimationPageState extends State<FilteredChildAnimationPage>
builder
=
(
BuildContext
context
,
Widget
child
)
=>
Transform
(
transform:
Matrix4
.
rotationZ
(
_controller
.
value
*
2.0
*
pi
),
alignment:
Alignment
.
center
,
filterQuality:
FilterQuality
.
low
,
child:
child
,
);
break
;
...
...
packages/flutter/lib/src/rendering/proxy_box.dart
View file @
ae12bf6b
...
...
@@ -2205,12 +2205,14 @@ class RenderTransform extends RenderProxyBox {
AlignmentGeometry
?
alignment
,
TextDirection
?
textDirection
,
this
.
transformHitTests
=
true
,
FilterQuality
?
filterQuality
,
RenderBox
?
child
,
})
:
assert
(
transform
!=
null
),
super
(
child
)
{
this
.
transform
=
transform
;
this
.
alignment
=
alignment
;
this
.
textDirection
=
textDirection
;
this
.
filterQuality
=
filterQuality
;
this
.
origin
=
origin
;
}
...
...
@@ -2264,6 +2266,9 @@ class RenderTransform extends RenderProxyBox {
markNeedsSemanticsUpdate
();
}
@override
bool
get
alwaysNeedsCompositing
=>
child
!=
null
&&
_filterQuality
!=
null
;
/// When set to true, hit tests are performed based on the position of the
/// child as it is painted. When set to false, hit tests are performed
/// ignoring the transformation.
...
...
@@ -2285,6 +2290,21 @@ class RenderTransform extends RenderProxyBox {
markNeedsSemanticsUpdate
();
}
/// The filter quality with which to apply the transform as a bitmap operation.
///
/// {@macro flutter.widgets.Transform.optional.FilterQuality}
FilterQuality
?
get
filterQuality
=>
_filterQuality
;
FilterQuality
?
_filterQuality
;
set
filterQuality
(
FilterQuality
?
value
)
{
if
(
_filterQuality
==
value
)
return
;
final
bool
didNeedCompositing
=
alwaysNeedsCompositing
;
_filterQuality
=
value
;
if
(
didNeedCompositing
!=
alwaysNeedsCompositing
)
markNeedsCompositingBitsUpdate
();
markNeedsPaint
();
}
/// Sets the transform to the identity matrix.
void
setIdentity
()
{
_transform
!.
setIdentity
();
...
...
@@ -2372,18 +2392,32 @@ class RenderTransform extends RenderProxyBox {
void
paint
(
PaintingContext
context
,
Offset
offset
)
{
if
(
child
!=
null
)
{
final
Matrix4
transform
=
_effectiveTransform
!;
final
Offset
?
childOffset
=
MatrixUtils
.
getAsTranslation
(
transform
);
if
(
childOffset
==
null
)
{
layer
=
context
.
pushTransform
(
needsCompositing
,
offset
,
transform
,
super
.
paint
,
oldLayer:
layer
as
TransformLayer
?,
);
if
(
filterQuality
==
null
)
{
final
Offset
?
childOffset
=
MatrixUtils
.
getAsTranslation
(
transform
);
if
(
childOffset
==
null
)
{
layer
=
context
.
pushTransform
(
needsCompositing
,
offset
,
transform
,
super
.
paint
,
oldLayer:
layer
is
TransformLayer
?
layer
as
TransformLayer
?
:
null
,
);
}
else
{
super
.
paint
(
context
,
offset
+
childOffset
);
layer
=
null
;
}
}
else
{
super
.
paint
(
context
,
offset
+
childOffset
);
layer
=
null
;
final
ui
.
ImageFilter
filter
=
ui
.
ImageFilter
.
matrix
(
transform
.
storage
,
filterQuality:
filterQuality
!,
);
if
(
layer
is
ImageFilterLayer
)
{
final
ImageFilterLayer
filterLayer
=
layer
!
as
ImageFilterLayer
;
filterLayer
.
imageFilter
=
filter
;
}
else
{
layer
=
ImageFilterLayer
(
imageFilter:
filter
);
}
context
.
pushLayer
(
layer
!,
super
.
paint
,
offset
);
}
}
}
...
...
packages/flutter/lib/src/widgets/basic.dart
View file @
ae12bf6b
...
...
@@ -1178,6 +1178,7 @@ class Transform extends SingleChildRenderObjectWidget {
this
.
origin
,
this
.
alignment
,
this
.
transformHitTests
=
true
,
this
.
filterQuality
,
Widget
?
child
,
})
:
assert
(
transform
!=
null
),
super
(
key:
key
,
child:
child
);
...
...
@@ -1215,6 +1216,7 @@ class Transform extends SingleChildRenderObjectWidget {
this
.
origin
,
this
.
alignment
=
Alignment
.
center
,
this
.
transformHitTests
=
true
,
this
.
filterQuality
,
Widget
?
child
,
})
:
transform
=
Matrix4
.
rotationZ
(
angle
),
super
(
key:
key
,
child:
child
);
...
...
@@ -1242,6 +1244,7 @@ class Transform extends SingleChildRenderObjectWidget {
Key
?
key
,
required
Offset
offset
,
this
.
transformHitTests
=
true
,
this
.
filterQuality
,
Widget
?
child
,
})
:
transform
=
Matrix4
.
translationValues
(
offset
.
dx
,
offset
.
dy
,
0.0
),
origin
=
null
,
...
...
@@ -1283,6 +1286,7 @@ class Transform extends SingleChildRenderObjectWidget {
this
.
origin
,
this
.
alignment
=
Alignment
.
center
,
this
.
transformHitTests
=
true
,
this
.
filterQuality
,
Widget
?
child
,
})
:
transform
=
Matrix4
.
diagonal3Values
(
scale
,
scale
,
1.0
),
super
(
key:
key
,
child:
child
);
...
...
@@ -1314,6 +1318,15 @@ class Transform extends SingleChildRenderObjectWidget {
/// Whether to apply the transformation when performing hit tests.
final
bool
transformHitTests
;
/// The filter quality with which to apply the transform as a bitmap operation.
///
/// {@template flutter.widgets.Transform.optional.FilterQuality}
/// The transform will be applied by re-rendering the child if [filterQuality] is null,
/// otherwise it controls the quality of an [ImageFilter.matrix] applied to a bitmap
/// rendering of the child.
/// {@endtemplate}
final
FilterQuality
?
filterQuality
;
@override
RenderTransform
createRenderObject
(
BuildContext
context
)
{
return
RenderTransform
(
...
...
@@ -1322,6 +1335,7 @@ class Transform extends SingleChildRenderObjectWidget {
alignment:
alignment
,
textDirection:
Directionality
.
maybeOf
(
context
),
transformHitTests:
transformHitTests
,
filterQuality:
filterQuality
,
);
}
...
...
@@ -1332,7 +1346,8 @@ class Transform extends SingleChildRenderObjectWidget {
..
origin
=
origin
..
alignment
=
alignment
..
textDirection
=
Directionality
.
maybeOf
(
context
)
..
transformHitTests
=
transformHitTests
;
..
transformHitTests
=
transformHitTests
..
filterQuality
=
filterQuality
;
}
}
...
...
packages/flutter/lib/src/widgets/transitions.dart
View file @
ae12bf6b
...
...
@@ -356,6 +356,7 @@ class ScaleTransition extends AnimatedWidget {
Key
?
key
,
required
Animation
<
double
>
scale
,
this
.
alignment
=
Alignment
.
center
,
this
.
filterQuality
,
this
.
child
,
})
:
assert
(
scale
!=
null
),
super
(
key:
key
,
listenable:
scale
);
...
...
@@ -373,6 +374,11 @@ class ScaleTransition extends AnimatedWidget {
/// an alignment of (0.0, 1.0).
final
Alignment
alignment
;
/// The filter quality with which to apply the transform as a bitmap operation.
///
/// {@macro flutter.widgets.Transform.optional.FilterQuality}
final
FilterQuality
?
filterQuality
;
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.ProxyWidget.child}
...
...
@@ -380,12 +386,10 @@ class ScaleTransition extends AnimatedWidget {
@override
Widget
build
(
BuildContext
context
)
{
final
double
scaleValue
=
scale
.
value
;
final
Matrix4
transform
=
Matrix4
.
identity
()
..
scale
(
scaleValue
,
scaleValue
,
1.0
);
return
Transform
(
transform:
transform
,
return
Transform
.
scale
(
scale:
scale
.
value
,
alignment:
alignment
,
filterQuality:
filterQuality
,
child:
child
,
);
}
...
...
@@ -449,6 +453,7 @@ class RotationTransition extends AnimatedWidget {
Key
?
key
,
required
Animation
<
double
>
turns
,
this
.
alignment
=
Alignment
.
center
,
this
.
filterQuality
,
this
.
child
,
})
:
assert
(
turns
!=
null
),
super
(
key:
key
,
listenable:
turns
);
...
...
@@ -466,6 +471,11 @@ class RotationTransition extends AnimatedWidget {
/// an alignment of (1.0, -1.0) or use [Alignment.topRight]
final
Alignment
alignment
;
/// The filter quality with which to apply the transform as a bitmap operation.
///
/// {@macro flutter.widgets.Transform.optional.FilterQuality}
final
FilterQuality
?
filterQuality
;
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.ProxyWidget.child}
...
...
@@ -473,11 +483,10 @@ class RotationTransition extends AnimatedWidget {
@override
Widget
build
(
BuildContext
context
)
{
final
double
turnsValue
=
turns
.
value
;
final
Matrix4
transform
=
Matrix4
.
rotationZ
(
turnsValue
*
math
.
pi
*
2.0
);
return
Transform
(
transform:
transform
,
return
Transform
.
rotate
(
angle:
turns
.
value
*
math
.
pi
*
2.0
,
alignment:
alignment
,
filterQuality:
filterQuality
,
child:
child
,
);
}
...
...
packages/flutter/test/widgets/transform_test.dart
View file @
ae12bf6b
...
...
@@ -388,6 +388,119 @@ void main() {
},
skip:
isBrowser
,
// due to https://github.com/flutter/flutter/issues/42767
);
testWidgets
(
'Transform.translate with FilterQuality produces filter layer'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Transform
.
translate
(
offset:
const
Offset
(
25.0
,
25.0
),
child:
const
SizedBox
(
width:
100
,
height:
100
),
filterQuality:
FilterQuality
.
low
,
),
);
expect
(
tester
.
layers
.
whereType
<
ImageFilterLayer
>().
length
,
1
);
});
testWidgets
(
'Transform.scale with FilterQuality produces filter layer'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Transform
.
scale
(
scale:
3.14159
,
child:
const
SizedBox
(
width:
100
,
height:
100
),
filterQuality:
FilterQuality
.
low
,
),
);
expect
(
tester
.
layers
.
whereType
<
ImageFilterLayer
>().
length
,
1
);
});
testWidgets
(
'Transform.rotate with FilterQuality produces filter layer'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Transform
.
rotate
(
angle:
math
.
pi
/
4
,
child:
const
SizedBox
(
width:
100
,
height:
100
),
filterQuality:
FilterQuality
.
low
,
),
);
expect
(
tester
.
layers
.
whereType
<
ImageFilterLayer
>().
length
,
1
);
});
testWidgets
(
'Transform layers update to match child and filterQuality'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Transform
.
rotate
(
angle:
math
.
pi
/
4
,
child:
const
SizedBox
(
width:
100
,
height:
100
),
filterQuality:
FilterQuality
.
low
,
),
);
expect
(
tester
.
layers
.
whereType
<
ImageFilterLayer
>(),
hasLength
(
1
));
await
tester
.
pumpWidget
(
Transform
.
rotate
(
angle:
math
.
pi
/
4
,
child:
const
SizedBox
(
width:
100
,
height:
100
),
),
);
expect
(
tester
.
layers
.
whereType
<
ImageFilterLayer
>(),
isEmpty
);
await
tester
.
pumpWidget
(
Transform
.
rotate
(
angle:
math
.
pi
/
4
,
filterQuality:
FilterQuality
.
low
,
),
);
expect
(
tester
.
layers
.
whereType
<
ImageFilterLayer
>(),
isEmpty
);
await
tester
.
pumpWidget
(
Transform
.
rotate
(
angle:
math
.
pi
/
4
,
child:
const
SizedBox
(
width:
100
,
height:
100
),
filterQuality:
FilterQuality
.
low
,
),
);
expect
(
tester
.
layers
.
whereType
<
ImageFilterLayer
>(),
hasLength
(
1
));
});
testWidgets
(
'Transform layers with filterQuality golden'
,
(
WidgetTester
tester
)
async
{
await
tester
.
pumpWidget
(
Directionality
(
textDirection:
TextDirection
.
ltr
,
child:
GridView
.
count
(
crossAxisCount:
3
,
children:
<
Widget
>[
Transform
.
rotate
(
angle:
math
.
pi
/
6
,
child:
Center
(
child:
Container
(
width:
100
,
height:
20
,
color:
const
Color
(
0xffffff00
))),
),
Transform
.
scale
(
scale:
1.5
,
child:
Center
(
child:
Container
(
width:
100
,
height:
20
,
color:
const
Color
(
0xffffff00
))),
),
Transform
.
translate
(
offset:
const
Offset
(
20.0
,
60.0
),
child:
Center
(
child:
Container
(
width:
100
,
height:
20
,
color:
const
Color
(
0xffffff00
))),
),
Transform
.
rotate
(
angle:
math
.
pi
/
6
,
child:
Center
(
child:
Container
(
width:
100
,
height:
20
,
color:
const
Color
(
0xff00ff00
))),
filterQuality:
FilterQuality
.
low
,
),
Transform
.
scale
(
scale:
1.5
,
child:
Center
(
child:
Container
(
width:
100
,
height:
20
,
color:
const
Color
(
0xff00ff00
))),
filterQuality:
FilterQuality
.
low
,
),
Transform
.
translate
(
offset:
const
Offset
(
20.0
,
60.0
),
child:
Center
(
child:
Container
(
width:
100
,
height:
20
,
color:
const
Color
(
0xff00ff00
))),
filterQuality:
FilterQuality
.
low
,
),
],
),
),
);
await
expectLater
(
find
.
byType
(
GridView
),
matchesGoldenFile
(
'transform_golden.BitmapRotate.png'
),
);
});
}
class
TestRectPainter
extends
CustomPainter
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment