Skip to content

[Impeller] non-uniform image filter blurs don't render correctly #149458

@gaaclarke

Description

@gaaclarke

Looks like we may be duplicating the sigma to make it uniform at some point. Things seem to render correctly at the renderer level.

expected result

Screenshot 2024-05-31 at 4 13 18 PM

seen result

Screenshot 2024-05-31 at 4 13 14 PM

reproduction code

code
import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  double rotate = 0.0;

  Widget makeRow(double cornerSize, double blurSize) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: <Widget>[
        CustomPaint(
          painter: _RRectPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 0.5,
            rotate: rotate,
          ),
          size: const Size(150, 150),
        ),
        CustomPaint(
          painter: _RRectPathPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 0.5,
            rotate: rotate,
          ),
          size: const Size(150, 150),
        ),
        CustomPaint(
          painter: _RRectPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 1.0,
            rotate: rotate,
          ),
          size: const Size(150, 150),
        ),
        CustomPaint(
          painter: _RRectPathPainter(
            cornerSize: cornerSize,
            blurSize: blurSize,
            scale: 1.0,
            rotate: rotate,
          ),
          size: const Size(150, 150),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: <Widget>[
            makeRow(10, 10),
            makeRow(10, 30),
            makeRow(10, 50),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                const Text('rotation'),
                Slider(
                  value: rotate,
                  min: 0.0,
                  max: pi * 2.0,
                  onChanged: (value) {
                    setState(() {
                      rotate = value;
                    });
                  },
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

class _RRectPainter extends CustomPainter {
  _RRectPainter({
    required this.cornerSize,
    required this.blurSize,
    required this.scale,
    required this.rotate,
  });

  final double cornerSize;
  final double blurSize;
  final double scale;
  final double rotate;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(size.width * 0.5, size.height * 0.5);
    canvas.rotate(rotate);
    canvas.translate(-size.width * 0.5, -size.height * 0.5);

    canvas.scale(scale, scale);
    double w = size.width / scale;
    double h = size.height / scale;
    double c = cornerSize / scale;

    Rect rect = Rect.fromLTRB(0, 0, w, h);
    RRect rrect = RRect.fromRectXY(rect, c, c);

    Paint paint = Paint()
      ..imageFilter = ImageFilter.blur(sigmaX: blurSize, sigmaY: 0.0, tileMode: TileMode.decal)
      // ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSize)
      ..color = Colors.blue;
    canvas.drawRRect(rrect, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

class _RRectPathPainter extends CustomPainter {
  _RRectPathPainter({
    required this.cornerSize,
    required this.blurSize,
    required this.scale,
    required this.rotate,
  });

  final double cornerSize;
  final double blurSize;
  final double scale;
  final double rotate;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(size.width * 0.5, size.height * 0.5);
    canvas.rotate(rotate);
    canvas.translate(-size.width * 0.5, -size.height * 0.5);

    canvas.scale(scale, scale);
    double w = size.width / scale;
    double h = size.height / scale;
    double c = cornerSize / scale;

    Path path = Path();
    path.addRect(Rect.fromLTRB(c, 0, w - c, h));
    path.addRect(Rect.fromLTRB(0, c, w, h - c));
    path.addOval(Rect.fromLTRB(0, 0, c * 2, c * 2));
    path.addOval(Rect.fromLTRB(w - c * 2, 0, w, c * 2));
    path.addOval(Rect.fromLTRB(0, h - c * 2, c * 2, h));
    path.addOval(Rect.fromLTRB(w - c * 2, h - c * 2, w, h));

    Paint paint = Paint()
      ..imageFilter = ImageFilter.blur(sigmaX: blurSize, sigmaY: 0.0, tileMode: TileMode.decal)
      // ..maskFilter = MaskFilter.blur(BlurStyle.normal, blurSize)
      ..color = Colors.blue;
    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}

doctor

[!] Flutter (Channel [user-branch], 3.23.0-13.0.pre.27, on macOS 14.5 23F79 darwin-arm64, locale en)
    ! Flutter version 3.23.0-13.0.pre.27 on channel [user-branch] at /Users/aaclarke/dev/flutter
      Currently on an unknown channel. Run `flutter channel` to switch to an official channel.
      If that doesn't fix the issue, reinstall Flutter by following instructions at https://flutter.dev/docs/get-started/install.
    ! Upstream repository unknown source is not a standard remote.
      Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss this error.
    • Framework revision 472a29d3f3 (31 hours ago), 2024-05-30 09:24:14 -0700
    • Engine revision fb64b9a4e6
    • Dart version 3.5.0 (build 3.5.0-203.0.dev)
    • DevTools version 2.36.0-dev.10
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.

cc @flar (discovered by him in flutter/engine#53130)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work liste: impellerImpeller rendering backend issues and features requeststeam-engineOwned by Engine teamtriaged-engineTriaged by Engine team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions