Заполнение многоугольников и областей
6.3. Заполнение с затравкой
Область, подлежащая заполнению, не всегда задается в виде многоугольника. В этом разделе мы рассмотрим случай, когда заполняемая область задается цветом своей границы. Множество пикселей на растре не задает область однозначно, поэтому требуется задать координаты "затравочного" пикселя, принадлежащего области.
Алгоритмы, рассматриваемые в этом разделе, используют структуру данных под названием стек. Стек содержит упорядоченный набор элементов и поддерживает две основные операции: добавить элемент и извлечь элемент. Вторая операция возвращает элемент, добавленный последним, и удаляет его из набора элементов. Программно стек может быть реализован на основе одномерного массива.
Простейший алгоритм заполнения с затравкой - это так называемый алгоритм короеда, получивший подобное название, поскольку заполняемая область последовательно "выедается" по одному пикселю. Он устроен следующим образом:
//Заполняет цветом A область, ограниченную цветом B Добавить затравочный пиксель в стек; while(стек не пуст) { P = стек.Извлечь(); //извлечение пикселя из стека Закрасить пиксель P; foreach(Q in соседние с P пиксели) if(цвет Q != B и Q еще не закрашен) стек.Добавить(Q) }Листинг 6.5. Алгоритм короеда
При обходе соседних пикселей может рассматриваться и 4-связность ( Q принимает 4 значения), и 8-связность ( Q принимает 8 значений). В зависимости от этого результат будет различным.
Внутри приведенного алгоритма производится проверка "Q еще не закрашен". Если известно, что пикселей цвета А внутри нашей области изначально не было, то это условие эквивалентно условию "цвет Q != A". В противном случае для проверки этого условия требуется введение специального буфера, где каждому пикселю соответствует флаг закраски, инициализируемый нулем и становящийся единицей при закраске пикселя.
Пример работы алгоритма показан на рис. 6.7. 4-связная область закрашивается в серый цвет и ограничена темными пикселями. Пиксель P извлечен из стека. Он закрашивается серым. Соседние незакрашенные пиксели Q, R и S добавляются в стек.
Используя пространственную когерентность, можно построить более эффективный алгоритм, использующий стек меньшей глубины и закрашивающий за одну итерацию целый горизонтальный отрезок пикселей:
//Заполняет цветом A область, ограниченную цветом B Добавить затравочный пиксель в стек; while(стек не пуст) { (X,Y) = стек.Извлечь(); //извлечение пикселя из стека X_min = X; while(цвет(X_min-1,Y) != B) X_min--; X_max = X; while(цвет(X_max+1,Y) != B) X_max++; Закрасить отрезок (X_min,Y)--(X_max,Y) цветом A; //обработка строки сверху флаг = 1; for(X = X_min; X <= X_max; X++) { if( цвет(X,Y-1) != B и (X,Y-1) еще не закрашен) { if( флаг == 1 ) { стек.Добавить(X,Y-1); флаг = 0; } } else флаг = 1; } //обработка строки снизу флаг = 1; for(X = X_min; X <= X_max; X++) { if( цвет(X,Y+1) != B и (X,Y+1) еще не закрашен) { if( флаг == 1 ) { стек.Добавить(X,Y+1); флаг = 0; } } else флаг = 1; } }Листинг 6.6. Заполнение с затравкой по отрезкам
Пример работы алгоритма показан на рис. 6.8. Пиксель P - затравочный; 4-связная область закрашивается в серый цвет и ограничена темными пикселями. При первой итерации производится заполнение целого отрезка. Пиксели Q и R на строке сверху и S на строке снизу добавляются в стек.
Заметим, что приведенные в данном разделе алгоритмы несложно переделать для перекрашивания с затравкой области постоянного цвета A в другой цвет B. Иными словами, для случая, когда область задается не цветом границы, а собственным цветом. В этом случае алгоритмы даже упрощаются: соседний пиксель добавляется в стек, только если он имеет цвет A (иначе он или не принадлежит перекрашиваемой области, или уже закрашен).