paint method

  1. @override
void paint(
  1. Canvas canvas,
  2. Offset screenOffset,
  3. Offset canvasOffset,
  4. double scale,
  5. Size canvasSize,
)
override

draw the backround on this context. Implement this to have different kinds of backgrounds screenOffset is the screen space offset for clipping canvasOffset is the grid space offset from the controller

Implementation

@override
void paint(
  Canvas canvas,
  Offset screenOffset,
  Offset canvasOffset,
  double scale,
  Size canvasSize,
) {
  // Always draw background fill (also serves as fallback while shader loads)
  final bgPaint = Paint()
    ..color = backgroundColor
    ..style = PaintingStyle.fill;
  final rect = Rect.fromLTWH(
    screenOffset.dx,
    screenOffset.dy,
    canvasSize.width,
    canvasSize.height,
  );

  // Ensure shader load started
  _ensureProgramLoaded();

  final program = _program;
  if (program == null) {
    // Fallback: just fill background without dots until shader is ready
    canvas.drawRect(rect, bgPaint);
    return;
  }

  // Compute uniforms in logical pixels to match FlutterFragCoord and CPU path
  final scaledSpacingPx = (spacing * scale).abs();
  final radiusPx = (size * scale).abs();

  // Phase to align grid with world origin under pan/zoom
  double modPositive(double a, double m) => ((a % m) + m) % m;
  final sign = naturalPan ? 1.0 : -1.0;
  final phaseXPx = modPositive(
    sign * canvasOffset.dx * scale,
    scaledSpacingPx == 0 ? 1 : scaledSpacingPx,
  );
  final phaseYPx = modPositive(
    sign * canvasOffset.dy * scale,
    scaledSpacingPx == 0 ? 1 : scaledSpacingPx,
  );

  // Snap origin: use exact logical origin (no rounding) to match CPU path
  final originXPx = screenOffset.dx;
  final originYPx = screenOffset.dy;

  // Precompute inverse spacing to avoid division in shader
  final invScaledSpacing = scaledSpacingPx == 0 ? 0.0 : 1.0 / scaledSpacingPx;

  // Build fragment shader and set uniforms in declaration order
  final shader = program.fragmentShader();
  int i = 0;
  void set1(double a) {
    shader.setFloat(i++, a);
  }

  void set2(double a, double b) {
    shader.setFloat(i++, a);
    shader.setFloat(i++, b);
  }

  void set4c(Color c) {
    shader.setFloat(i++, c.r);
    shader.setFloat(i++, c.g);
    shader.setFloat(i++, c.b);
    shader.setFloat(i++, c.a);
  }

  set2(originXPx, originYPx);
  set1(scaledSpacingPx);
  set1(invScaledSpacing);
  set2(phaseXPx, phaseYPx);
  set1(radiusPx);
  set4c(dotColor);
  set4c(backgroundColor);

  final paint = Paint()..shader = shader;

  // Draw once with the shader
  canvas.drawRect(rect, paint);
}