floodFill function

List<Point<int>> floodFill(
  1. Artifact binaryPixels,
  2. Artifact visited,
  3. int startX,
  4. int startY,
)

Performs a highly optimized flood fill algorithm on a binary image matrix.

Uses direct array access and efficient data structures (8-connected).

Returns a list of Point objects representing all connected points found.

Implementation

List<Point<int>> floodFill(
  final Artifact binaryPixels,
  final Artifact visited,
  final int startX,
  final int startY,
) {
  final int width = binaryPixels.cols;
  final int height = binaryPixels.rows;

  // Early bounds check
  if (startX < 0 || startX >= width || startY < 0 || startY >= height) {
    return const [];
  }

  // Early check for valid starting pixel
  if (!binaryPixels.cellGet(startX, startY)) {
    return const [];
  }

  // Direct access to the underlying arrays
  final Uint8List pixelData = binaryPixels.matrix;
  final Uint8List visitedData = visited.matrix;

  // Pre-allocate with estimated capacity to reduce reallocations
  final List<Point<int>> connectedPoints = <Point<int>>[];
  final Queue<int> queue = Queue<int>();

  // Calculate initial index
  final int startIndex = startY * width + startX;

  // Mark start point as visited and add to queue
  visitedData[startIndex] = _pixelOnValue;
  queue.add(startIndex);
  connectedPoints.add(Point(startX, startY));

  // Direction offsets for adjacent pixels (including diagonals)
  const List<int> rowOffsets = [-1, -1, -1, 0, 0, 1, 1, 1];
  const List<int> colOffsets = [-1, 0, 1, -1, 1, -1, 0, 1];

  while (queue.isNotEmpty) {
    final int currentIndex = queue.removeFirst();
    final int x = currentIndex % width;
    final int y = currentIndex ~/ width;

    // Check all eight directions (including diagonals)
    for (int i = 0; i < rowOffsets.length; i++) {
      final int nx = x + colOffsets[i];
      final int ny = y + rowOffsets[i];

      // Skip out-of-bounds
      if (nx < 0 || nx >= width || ny < 0 || ny >= height) {
        continue;
      }

      final int neighborIndex = ny * width + nx;

      // Check if neighbor is valid and not visited
      if (pixelData[neighborIndex] == _pixelOnValue &&
          visitedData[neighborIndex] == _pixelOffValue) {
        visitedData[neighborIndex] = _pixelOnValue;
        queue.add(neighborIndex);
        connectedPoints.add(Point(nx, ny));
      }
    }
  }

  return connectedPoints;
}