-
Notifications
You must be signed in to change notification settings - Fork 29.7k
Description
Steps to reproduce
- Create a project and paste the dart code below into
main.dart - Create the
shadersfolder and theone.fragfile - Paste the GLSL code below into
one.frag - Add the shader path to
pubspec.yaml - Run using the device with impeller
I have tested it on the main branch channel (3.33.0-1.0.pre.619) using Samsung Galaxy A51 (Android 12)
Expected results
ImageFilter.shader in ImageFilter.compose (with ImageFiltered and BackdropFilter widgets) should work for the inner and outer attributes.
Actual results
ImageFilter.shader in ImageFilter.compose only works for the outer parameter, not for the inner one.
For example, this case will work:
ImageFiltered(
imageFilter: ImageFilter.compose(
inner: ImageFilter.blur(sigmaX: 1.0),
outer: ImageFilter.shader(shader),
),
child: child
)but in this case, nothing is displayed:
ImageFiltered(
imageFilter: ImageFilter.compose(
outer: ImageFilter.blur(sigmaX: 1.0),
inner: ImageFilter.shader(shader),
),
child: child
)In addition, even when using Image.shader as the outer parameter with ImageFilter.matrix as the inner one, and the matrix uses scaling, the resulting texture is too small to represent the full new image (shown in the picture below).
Code sample
Code sample
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show Ticker;
enum ImageFilterMode {
outerShaderInnerBlurWorks,
innerShaderOuterBlurNotWorks,
outerShaderInnerShaderNotWorks,
outerShaderInnerMatrixWorksWithProblem,
innerShaderOuterMatrixNotWorks,
}
void main() {
runApp(App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(debugShowCheckedModeBanner: false, home: Home());
}
}
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> with TickerProviderStateMixin {
late FragmentProgram program;
FragmentShader? shader;
Ticker? ticker;
ImageFilterMode mode = ImageFilterMode.outerShaderInnerBlurWorks;
@override
void initState() {
super.initState();
FragmentProgram.fromAsset('shaders/one.frag').then((program) {
this.program = program;
setState(() {
shader = program.fragmentShader();
});
ticker = createTicker((Duration elapsed) {
shader!.setFloat(2, elapsed.inMilliseconds.toDouble());
setState(() {});
})..start();
});
}
ImageFilter _getImageFilter() {
return switch (mode) {
ImageFilterMode.outerShaderInnerBlurWorks => ImageFilter.compose(
outer: ImageFilter.shader(shader!),
inner: ImageFilter.blur(sigmaX: 1, sigmaY: 1),
),
ImageFilterMode.innerShaderOuterBlurNotWorks => ImageFilter.compose(
inner: ImageFilter.shader(shader!),
outer: ImageFilter.blur(sigmaX: 1, sigmaY: 1),
),
ImageFilterMode.outerShaderInnerShaderNotWorks => ImageFilter.compose(
inner: ImageFilter.shader(shader!),
outer: ImageFilter.shader(shader!),
),
ImageFilterMode.outerShaderInnerMatrixWorksWithProblem =>
ImageFilter.compose(
outer: ImageFilter.shader(shader!),
inner: ImageFilter.matrix(
(Matrix4.identity()
..translate(100.0, 50.0)
..scale(0.5)
..translate(-100.0, -50.0))
.storage,
),
),
ImageFilterMode.innerShaderOuterMatrixNotWorks => ImageFilter.compose(
inner: ImageFilter.shader(shader!),
outer: ImageFilter.matrix(
(Matrix4.identity()
..translate(100.0, 50.0)
..scale(0.5)
..translate(-100.0, -50.0))
.storage,
),
),
};
}
@override
void dispose() {
ticker?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (shader == null) {
return const SizedBox();
}
return SafeArea(
child: Scaffold(
body: Center(
child: Column(
spacing: 100,
mainAxisAlignment: MainAxisAlignment.center,
children: [
DropdownButton<ImageFilterMode>(
items: ImageFilterMode.values
.map(
(item) =>
DropdownMenuItem(value: item, child: Text(item.name)),
)
.toList(),
value: mode,
onChanged: (value) {
if (value != null && value != mode) {
setState(() {
mode = value;
});
}
},
),
SizedBox(
width: 200,
height: 100,
child: ColoredBox(
color: Colors.blue.withAlpha(50),
child: ImageFiltered(
imageFilter: _getImageFilter(),
child: Column(
spacing: 20,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.flutter_dash),
Text('Hello, world'),
],
),
),
),
),
],
),
),
),
);
}
}
#include <flutter/runtime_effect.glsl>
uniform vec2 uSize;
uniform float uTime;
uniform sampler2D uTexture;
out vec4 fragColor;
void main() {
vec2 uv = FlutterFragCoord().xy / uSize;
#ifdef IMPELLER_TARGET_OPENGLES
uv.y = 1.0 - uv.y;
#endif
fragColor = texture(uTexture, uv + vec2(0.2 * sin(uv.x + uv.y + 0.0025*uTime), 0.09 * sin(10*uv.x + 0.0025*uTime)));
fragColor += vec4(1.0, 0.0, 0.0, 0.25);
}Screenshots or Video
Screenshots / Video demonstration
outer - ImageFilter.shader, inner - ImageFilter.blur - no problems:

outer - ImageFilter.blur, inner - ImageFilter.shader - nothing is shown:

outer - ImageFilter.shader , inner - ImageFilter.shader - nothing is shown:

outer - ImageFilter.shader , inner - ImageFilter.matrix - we can see a small texture that clips our widget:

outer - ImageFilter.matrix, inner - ImageFilter.shader - nothing is shown:

Flutter Doctor output
Doctor output
[√] Flutter (Channel main, 3.33.0-1.0.pre.619, on Microsoft Windows [Version 10.0.19045.5965], locale ru-RU)
[√] Windows Version (Њ ©Єа®б®дв Windows 10 Pro 64-а §ап¤ п, 22H2, 2009)
[√] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Build Tools 2022 17.6.5)
[√] Android Studio (version 2022.2)
[√] VS Code (version 1.101.1)
[√] Connected device (4 available)
[√] Network resources
• No issues found!