zathras/jovial_svg

Incorrect rendering on Flutter Web with HTML renderer (default for mobile browser)

lcm1475 opened this issue · 8 comments

flutter sdk: 3.0.5
ios:ok
android:ok
flutter web pc : ok
flutter web mobile : unusual
25
18

OK, the inconsistent performance you're seeing is on flutter web mobile. Can you provide some performance measurements, please? What precisely are you observing?

Are you saying that the performance on flutter web mobile is inconsistent from one run to another? Or are you saying that the performance you observe on flutter web mobile is slower than the performance on a PC, by more than you'd expect?

No performance problems

The rendering is completely different. You can take a closer look at the place where I marked the arrow. The same SVG display is completely different

You can try this URL
https://uchinoko-maker.jp/assets/images/1b43faea723c65023ddc.svg
On the PC browser and the mobile phone browser, the performance is completely different

Interesting! So, the rendering is incorrect when running on flutter web, but only with a mobile browser.

I've verified this behavior:

  • MacOS Native: Rendering OK
  • Firefox and Chrome, flutter web, MacOS: OK
  • iOS Simulator running Safari, flutter web: Bad rendering

A minimal SVG asset that has the same problem would be helpful, if you can come up with one.

This could be a bug in jovial_svg, but I think it's at least as likely that it's a bug in the underlying platform, either flutter web, or the implementation of the Canvas API in mobile browsers. In the past, jovial_svg has uncovered other bugs in the flutter platform.

Looking at the images, my first guess is that the problem has something to do with masks. Perhaps current mobile browsers don't implement this part of the Canvas API? Anyway, with a little bit of trial-and-error, I bet I can make a small (~ 10 line) SVG that reproduces the problem, and from there figure out if it's an issue in jovial_svg, or in the underlying platform.

Yes, it is an issue with a clip path. It can be reproduced with this svg file:

<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
  <clipPath id="clip">
    <path fill="aqua" id="star" d="M 50,5 L65,60 26,32 74,32 35,60 z" />
  </clipPath>
</defs>
<rect x="0" y="0" width="100" height="100" fill="darkgreen"
    clip-path="url(#clip)" />
</svg>

My informed guess is that the API for compositing layers isn't implemented in the mobile web browser(s), for whatever reason. I'll write a minimal stand-alone program to reproduce this behavior. Assuming it does turn out to be a platform limitation, I'll file it as a bug against the flutter web runtime, and link to that here.

By default, flitter build web switches different rendering engines according to different environments

flutter build web --web-renderer canvaskit

Using this command, you can force canvas to render, and everything will become zh


from flutter :

By default, the flutter build and flutter run commands use the auto choice for the web renderer. This means that your app runs with the HTML renderer on mobile browsers and CanvasKit on desktop browsers. This is our recommended combination to optimize for the characteristics of each platform.

For more information, see Web renderers.

Ah, interesting that they use a different default renderer for mobile.

I verified, and this is indeed a bug in the HTML renderer. The open bug I found was filed in 2020: flutter/flutter#48417 . I added a report of this to that bug.

Here's a fairly minimal application to reproduce the issue:

import 'package:flutter/material.dart';
// import 'package:flutter/services.dart';
// import 'package:jovial_svg/jovial_svg.dart';

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

class MinimalSample extends StatelessWidget {
  const MinimalSample({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: "Canvas.saveLayer doesn't work with HTML renderer",
        /*
        home: ScalableImageWidget.fromSISource(
            si: ScalableImageSource.fromSvg(rootBundle, 'assets/clip_path.svg')));
        */
        home:  CustomPaint(painter: Painter(), size: const Size(400, 400)));
  }
}

class Painter extends CustomPainter {

  @override
  void paint(Canvas c, Size size) {
    final p = Paint();
    p.style = PaintingStyle.fill;
    p.color = const Color(0xffffffff);
    c.drawCircle(const Offset(200, 200), 180, p);
    c.saveLayer(null, Paint()..blendMode = BlendMode.srcIn);
    p.color = const Color(0xff006400);
    c.drawRect(Rect.fromLTWH(0, 0, 400, 400), p);
    c.restore();
  }

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