Repository: flutter
Issue: Canvas.drawVertices broken on web for mobile
Labels: engine,a: fidelity,platform-web,c: rendering,e: web_html,P2,team-web,triaged-web

While trying to add some custom graphics to my app, I ran into some pretty big issues with the drawVertices method. The drawVertices method does not function correctly with the HTML renderer, which is the default when viewing the web page on a mobile device. See the sample code and screenshots below.

Update: This issue used to reference anti-aliasing issues. Since this is a separate problem, it has been moved to a new issue: #77485

## Steps to Reproduce

1. Create an array of points, such as for drawing a polygon.
2. Use drawPath to draw a Path that contains a polygon with those points.
3. Use drawVertices to draw a Vertices object with those same points.
4. Build the Flutter app for web using the HTML renderer (the default for mobile).

**Expected results:**
Both drawings should be practically identical.

**Actual results:**
When the web build is viewed from a mobile device, it is very distorted.

<details>
  <summary>Logs</summary>
flutter analyze

```
Analyzing vertices_test...
No issues found! (ran in 2.1s)
```

flutter doctor -v

```
[√] Flutter (Channel dev, 2.1.0-10.0.pre, on Microsoft Windows [Version 10.0.19042.844], locale en-US)
    • Flutter version 2.1.0-10.0.pre at d:\Developer\flutter
    • Framework revision cc9b78fc5c (8 days ago), 2021-02-25 13:26:03 -0800
    • Engine revision a252ec09b7
    • Dart version 2.13.0 (build 2.13.0-77.0.dev)

[√] Android toolchain - develop for Android devices (Android SDK version 30.0.1)
    • Android SDK at D:\Developer\Android\SDK
    • Platform android-30, build-tools 30.0.1
    • ANDROID_SDK_ROOT = D:\Developer\Android\SDK
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)
    • All Android licenses accepted.

[√] Chrome - develop for the web
    • Chrome at C:\Program Files (x86)\Google\Chrome\Application\chrome.exe

[√] Visual Studio - develop for Windows (Visual Studio Community 2019 16.8.5)
    • Visual Studio at C:\Program Files (x86)\Microsoft Visual Studio\2019\Community
    • Visual Studio Community 2019 version 16.8.31005.135
    • Windows 10 SDK version 10.0.18362.0

[√] Android Studio (version 4.1.0)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin can be installed from:
       https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
       https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b01)

[√] VS Code, 64-bit edition (version 1.52.1)
    • VS Code at C:\Program Files\Microsoft VS Code
    • Flutter extension version 3.19.0

[√] Connected device (3 available)
    • Windows (desktop) • windows • windows-x64    • Microsoft Windows [Version 10.0.19042.844]
    • Chrome (web)      • chrome  • web-javascript • Google Chrome 89.0.4389.72
    • Edge (web)        • edge    • web-javascript • Microsoft Edge 88.0.705.81

• No issues found!
```

</details>

<details>
  <summary>Sample Code</summary>

```dart
import 'dart:math';
import 'dart:ui';

import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

enum PaintMethod {
  /// Wireframe simply renders an outline of what the shape should be.
  wireframe,

  /// Path uses Path.addPolygon to draw a path.
  path,

  /// Vertices uses the drawVertices method.
  /// When the app is running in the browser of a mobile device, this does not work correctly.
  vertices,
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Vertex Test',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: MyHomePage(
        shape: [
          /// Creates a 15 sided polygon
          for (int i = 0; i < 16; i++)
            Offset(50, 50) + Offset.fromDirection(2 * pi * (i / 15), 30),
        ],
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.shape = const []}) : super(key: key);

  /// The shape to be painted.
  final List<Offset> shape;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  /// The method to use when painting.
  PaintMethod method = PaintMethod.wireframe;

  void setMethod(PaintMethod value) => setState(() => method = value);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Vertex Test'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(
              child: CustomPaint(
                painter: VertexPainter(widget.shape, method),
                child: Container(),
              ),
            ),
            Row(
              children: [
                Radio<PaintMethod>(
                  value: PaintMethod.wireframe,
                  groupValue: method,
                  onChanged: (v) => setMethod(v!),
                ),
                Expanded(child: Text('Wireframe')),
                Radio<PaintMethod>(
                  value: PaintMethod.path,
                  groupValue: method,
                  onChanged: (v) => setMethod(v!),
                ),
                Expanded(child: Text('Path')),
                Radio<PaintMethod>(
                  value: PaintMethod.vertices,
                  groupValue: method,
                  onChanged: (v) => setMethod(v!),
                ),
                Expanded(child: Text('Vertices')),
              ],
            )
          ],
        ),
      ),
    );
  }
}

class VertexPainter extends CustomPainter {
  const VertexPainter(this.positions, this.method);

  /// Positions should be between (0,0) and (100,100)
  final List<Offset> positions;

  /// The method to use when painting
  final PaintMethod method;

  @override
  void paint(Canvas canvas, Size size) {
    canvas.save();
    canvas.scale(size.width / 100, size.height / 100);
    final paint = Paint()..color = Colors.red;

    switch (method) {
      case PaintMethod.wireframe:
        canvas.drawPoints(PointMode.polygon, positions, paint);
        break;
      case PaintMethod.path:
        canvas.drawPath(Path()..addPolygon(positions, false), paint);
        break;
      case PaintMethod.vertices:
        canvas.drawVertices(
          Vertices(VertexMode.triangleFan, positions),
          BlendMode.srcOver,
          paint,
        );
    }
    canvas.restore();
  }

  @override
  bool shouldRepaint(VertexPainter old) =>
      old.positions != positions || old.method != method;
}
```

</details>

<details>
  <summary>Screenshots</summary>
These screenshots are from the sample code above. The drawPoints method is used to give a wireframe reference point.

### Web - Desktop
| drawPoints | drawPath | drawVertices |
| --- | --- | --- |
| ![desktop-web-wireframe](https://user-images.githubusercontent.com/14063628/110157777-715a1a80-7d9d-11eb-81cf-35e5086b801d.png) | ![desktop-web-path](https://user-images.githubusercontent.com/14063628/110157794-77e89200-7d9d-11eb-9bd4-f48ec5b36429.png) | ![desktop-web-vertices](https://user-images.githubusercontent.com/14063628/110157804-79b25580-7d9d-11eb-9b25-60e98f3689ee.png) |

### Web - Mobile
| drawPoints | drawPath | drawVertices |
| --- | --- | --- |
| ![mobile-web-wireframe](https://user-images.githubusercontent.com/14063628/110157832-83d45400-7d9d-11eb-9ad7-59f067726c70.jpg) | ![mobile-web-path](https://user-images.githubusercontent.com/14063628/110157842-8767db00-7d9d-11eb-85e5-beeba6ea04cf.jpg) | ![mobile-web-vertices](https://user-images.githubusercontent.com/14063628/110157860-8a62cb80-7d9d-11eb-9a92-ab93f03c4cb0.jpg) |

</details>