paint method
void
paint()
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);
}