Skip to content

Size difference between Impeller blur and legacy (Skia) blur #149943

@flar

Description

@flar

cc @gaaclarke

Testing against the fixes in flutter/engine#53261 the main issue of the orientation of unidirectional blurs is fixed, but there is still an inconsistency in the size of the directional blurs as can be seen in this test case:

test case
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 = 45.0 * pi / 180.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;
}
Skia output Skia Screenshot 2024-06-07 at 4 57 16 PM
Impeller output Impeller Screenshot 2024-06-07 at 4 58 05 PM
animated comparison

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work liste: impellerImpeller rendering backend issues and features requestsengineflutter/engine related. See also e: labels.found in release: 3.23Found to occur in 3.23has reproducible stepsThe issue has been confirmed reproducible and is ready to work onteam-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