flutterflutter-canvasflutter-custompainter

Flutter canvas draw raw points with different colors


I am trying to draw a lot of points to the screen quickly. I have created the following method.

void _paintPoints(Canvas canvas, List matrix, Size size) {
  final width = size.width / matrix.length;
  final height = size.height / matrix.first.length;
  Float32List points = Float32List(matrix.length * matrix.length * 2);
  final pointColors = <Color>[];

  for (var i = 0; i < matrix.length; i++) {
    for (var j = 0; j < matrix[i].length; j++) {
      final x = i * width;
      final y = j * height;
      final index = (i * matrix.length + j) * 2;

      points[index] = x;
      points[index + 1] = y;

      Color color = Color.fromARGB(
          matrix[i][j][3], matrix[i][j][0], matrix[i][j][1], matrix[i][j][2]);
      pointColors.add(color);
    }
  }

  canvas.drawRawPoints(
    PointMode.points,
    points,
    paint
      ..blendMode = BlendMode.srcOver
      ..strokeWidth = 2.0,
  );
}

This method is able to draw the points quickly but all of the points are the same color (The pointColors are not used). Is there away to use drawRawPoints() or some other Canvas method to draw a lot of points (tens of thousands) quickly with specific colors?

I have tried drawing each point separately, but it was too slow.


Solution

  • Can you try this?

    void _paintPoints(Canvas canvas, List matrix, Size size) {
      final width = size.width / matrix.length;
      final height = size.height / matrix.first.length;
    
      final Map<Color, Set<Offset>> map = HashMap();
    
      for (var i = 0; i < matrix.length; i++) {
        for (var j = 0; j < matrix[i].length; j++) {
          final offset = Offset(i * width, j * height);
          final color = Color.fromARGB(
              matrix[i][j][3], matrix[i][j][0], matrix[i][j][1], matrix[i][j][2]);
          map.putIfAbsent(color, () => HashSet()).add(offset);
        }
      }
    
      for (var e in map.entries) {
        canvas.drawRawPoints(
          PointMode.points,
          Float32List.fromList(e.value.expand((e) => [e.dx, e.dy]).toList()),
          paint
            ..color = e.key
            ..blendMode = BlendMode.srcOver
            ..strokeWidth = 2.0,
        );
      }
    }
    

    I am sorry I had to post it as answer. It is a try out code. I would suggest to cache the map result and use diff of upcoming matrix and size to calculate new map values.

    Edit: I did some editing and it should run fast. The idea here is to minimize memaddr calculation.

    Updated code

    void _paintPoints(Canvas canvas, List matrix, Size size) {
      final r = matrix.length;
      final c = matrix.first.length;
      final width = size.width / r;
      final height = size.height / c;
    
      final Map<Color, Set<Offset>> map = HashMap();
    
      for (var i = 0; i < r; i++) {
        final row = matrix[i];
        for (var j = 0; j < c; j++) {
          final cell = row[j];
          final offset = Offset(i * width, j * height);
          final color = Color.fromARGB(cell[3], cell[0], cell[1], cell[2]);
          map.putIfAbsent(color, () => HashSet()).add(offset);
        }
      }
    
      for (var e in map.entries) {
        canvas.drawPoints(
          PointMode.points,
          e.value.toList(),
          paint
            ..color = e.key
            ..blendMode = BlendMode.srcOver
            ..strokeWidth = 2.0,
        );
      }
    }
    

    Now the creation of map should be much faster. I did some benchmarking on my own and found out accessing 3d list with i and j takes more time than having a reference to it. Also, changed rowpoints to points. You could change it back.