blend2D function

List<Color> blend2D(
  1. int width,
  2. int height,
  3. double angle,
  4. List<Color> stops, {
  5. required bool hasDarkBackground,
})

Blends a series of Color stops into a 2D gradient.

Returns colors in row-major order: index = y * width + x.

This is a minimal-first port of lipgloss v2 Blend2D (but uses RGB interpolation via blend1D).

Implementation

List<Color> blend2D(
  int width,
  int height,
  double angle,
  List<Color> stops, {
  required bool hasDarkBackground,
}) {
  if (width < 1) width = 1;
  if (height < 1) height = 1;

  // Normalize angle to 0-360.
  angle %= 360;
  if (angle < 0) angle += 360;

  if (stops.isEmpty) return const [];
  if (stops.length == 1) {
    return List<Color>.filled(width * height, stops.first, growable: false);
  }

  final diagonalGradient = blend1D(
    math.max(width, height),
    stops,
    hasDarkBackground: hasDarkBackground,
  );
  if (diagonalGradient.isEmpty) return const [];

  final out = List<Color>.filled(width * height, diagonalGradient.first);

  final centerX = (width - 1) / 2.0;
  final centerY = (height - 1) / 2.0;

  final angleRad = angle * math.pi / 180.0;
  final cosAngle = math.cos(angleRad);
  final sinAngle = math.sin(angleRad);

  final diagonalLength = math.sqrt(width * width + height * height);
  final gradLen = diagonalGradient.length - 1;

  for (var y = 0; y < height; y++) {
    final dy = y - centerY;
    for (var x = 0; x < width; x++) {
      final dx = x - centerX;
      final rotX = dx * cosAngle - dy * sinAngle;
      final pos = ((rotX + diagonalLength / 2.0) / diagonalLength).clamp(
        0.0,
        1.0,
      );
      var idx = (pos * gradLen).floor();
      if (idx < 0) idx = 0;
      if (idx > gradLen) idx = gradLen;
      out[y * width + x] = diagonalGradient[idx];
    }
  }

  return out;
}