Закраска области, заданной цветом границы

Рассмотрим область, ограниченную набором пикселей заданного цвета и точку (x, y), лежащую внутри этой области.

Задача заполнения области заданным цветом в случае, когда эта область не является выпуклой, может оказаться довольно сложной.

Приведем простейший рекурсивный алгоритм заполнения области:

 

private void MyPixelFill(int x, int y, Color border_color, Color c_color)

{

Color c = bmp.GetPixel(x, y);

if ((c.ToArgb() != border_color.ToArgb()) &&

(c.ToArgb() != c_color.ToArgb()))

{

bmp.SetPixel(x, y, c_color);

MyPixelFill(x - 1, y, border_color, c_color);

MyPixelFill(x + 1, y, border_color, c_color);

MyPixelFill(x, y - 1, border_color, c_color);

MyPixelFill(x, y + 1, border_color, c_color);

}

}

Заметим, что корректное использование метода GetPixel возможно, если предварительно объекты типа Graphics и Bitmap связаны и инициализируются следующим образом:

bmp = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);

gr = Graphics.FromImage(bmp);

Этот алгоритм является слишком неэффективным, так как для всякого уже отрисованного пикселя функция вызывается ещё 4 раза и, кроме того, этот алгоритм требует слишком большого объёма стека из-за большой глубины рекурсии. Поэтому для решения задачи закраски области предпочтительнее алгоритмы, способные обрабатывать сразу целые группы пикселей, т. е. использовать их «связность» или «когерентность». Если данный пиксель принадлежит области, то, скорее всего, его ближайшие соседи также принадлежат данной области.

Группой таких пикселей обычно выступает полоса, определяемая правым пикселем. Для хранения правых определяющих пикселей используется стек. Словесно опишем улучшенный алгоритм, использующий когерентность пикселей.

Сначала заполняется горизонтальная полоса пикселей, содержащих начальную точку. Затем, чтобы найти самый правый пиксель каждой строки, справа налево проверяется строка, предыдущая по отношению к только что заполненной полосе. Адреса найденных пикселей заносятся в стек. То же самое выполняется и для строки, следующей и за последней заполненной полосой. Когда строка обработана таким способом, в качестве новой начальной точки используется пиксель, адрес которого берется из стека. Для него повторяется вся описанная процедура. Алгоритм заканчивает свою работу, если стек пуст.