Алгоритмы растеризации

Прежде чем перейдем к непосредственному рассмотрению возможности перевода ма­тематического описания объекта (линии и пр.) в растровую форму рассмотрим понятие связанности. Связность – возможность соедине­ния двух пикселей растровой линией, т. е. последовательным набором пик­селей. Возникает вопрос, когда пиксели (x1, y1) и (x2, y2) можно считать со­седними. Для этого вводятся два понятия связности:

1. 4-связность: пиксели считаются соседними, если либо их x-координаты, либо их y – координаты отличаются на единицу:

|x1­ – x2| + |y1 – y2| ≤ 1;

2. 8-связность: пиксели считаются соседними, если их x-координаты и y-координаты отличаются не более чем на единицу:

|x1­ – x2| ≤ 1, |y1 – y2| ≤ 1.

На рис. 2.2 изображены 4-связная линия (а) и 8-связная линия (б).

Рис. 2.2 4-связная линия (а) и 8-связная линии

При переводе объектов в растровое представление существуют, алгоритмы, как использующие 4-связанность, так использующие 8-связон­ность.

2.1.1. Растровое представление отрезка. Алгоритм Бре­зенхейма

Рассмотрим задачу построения растрового изображения отрезка, соеди­няющего точки A(xa, ya) и B(xb, yb). Для простоты будем считать, что

0 ≤ yb – yaxb – xa . Тогда отрезок описывается уравнением:

y = ya + (x–xa), x Є [xa, xb], или y = kx + b

Отсюда получаем простейший алгоритм растрового представления от­резка:

void line(int xa, int ya, int xb, int yb, int color)

{

double k = ((double)(yb – ya)) / (xb – xa);

double b = ya – k * xa;

for (int x = xa; x <= xb; x++)

putpixel(x, (int)(k * x + b), color);

}

Вычислений значений функции y = kx + b можно избежать, используя в цикле рекуррентные соотношения, так как при изменении x на 1 значение y меняется на k.

Void line(int xa, int ya, int xb, int yb, int color)

{

double k = ((double)(yb – ya)) / (xb – xa);

double y = ya;

for (int x = xa; x <= xb; x++, y += k)

putpixel(x, (int)y, color);

}

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

1. Выполняют операции над числами с плавающей точкой, а жела­тельно было бы работать с целочислен­ной арифметикой;

2. На каждом шаге выполняется операция округления, что также снижает быстро­действие.

Эти недостатки устранены в следующем алгоритме Брезенхейма.

Как и в предыдущем случае будем считать, что тангенс угла наклона от­резка принимает значение в диапазоне от 0 до 1. Рассмотрим i шаг алго­ритма (Рис 2.3.). На этом этапе пиксель Pi-1 уже найден как ближайший к ре­аль­ному отрезку. Требуется определить ка­кой из пикселов Ti или Si будет ус­тановлен следующим.

 

Рис. 2.3 i шаг алгоритма Брезенхейма

В алгоритме используется управляющая переменная di, которая на каждом шаге про­порциональна разности ме­жду S и T. Если S < T, то Si ближе к от­резку, иначе выбирается Ti.

Пусть изображаемый отрезок проходит из точки (x1, y1) в точку (x2, y2). Ис­ходя из на­чальных условий точка (x1, y1) ближе к началу координат. Тогда перенесем оба конца от­резка с помощью преобразования T(­­­­­­–x1, –y1), так чтобы первый конец отрезка совпал с началом координат. Начальной точкой отрезка стала точка (0, 0), конечной точкой (dx, dy), где dx = x2 – x1, dy = y2 – y1 (Рис 2.4.).

Рис. 2.4 Вид отрезка после переноса в начало координат

Уравнение прямой в этом случае будет иметь вид:

y=x

Обозначим координаты точки Pi-1 после переноса через (r, q). Тогда Si = (r+1, q) и Ti = (r+1, q+1).

Из подобия треугольников на рис 2.4. можно записать

=

Выразим S

S = (r + 1) – q

T можно представить как T = 1 – S. Используем предыдущую формулу

T = 1 – S = 1 – (r + 1) – q

Найдем разницу ST

ST = (r + 1) – q – 1 + (r + 1) – q = 2 (r + 1) – 2 q – 1

Помножим левую и правую часть на dx

dx (ST) = 2 dy (r + 1) – 2 q dx – dx = 2(r dy – q dx) + 2 dy – dx

Величина dx положительная, поэтомунеравенство dx (ST) < 0 можно ис­пользовать в качестве проверки при вы­боре Si. Обозначим di = dx (ST), тогда

 

di = 2(r dy – q dx) + 2 dy – dx

Поскольку r = xi-1 и q = yi-1, то

 

di = 2 xi-1 dy –2 yi-1 dx + 2 dy – dx

Прибавляя 1 к каждому индексу найдем di+1

 

di+1 = 2 xi dy –2 yi dx + 2 dy – dx

Вычитая di из di+1 получим

 

di+1di = 2 dy (xi xi-1) – 2 dx (yi yi-1)

 

Известно, что xi xi-1 = 1, тогда

 

di+1di = 2 dy – 2 dx (yi yi-1)

 

Отсюда выразим di+1

 

di+1 = di + 2 dy – 2 dx (yi yi-1)

 

Таким образом, получили итеративную формулу вычисления управляющего коэффициента di+1, по предыдущему значению di. С помощью управляющего коэффициента выбирается следующий пиксель Si или Ti.

Если di ≥ 0, тогда выбирается Ti и yi = yi–1 + 1 и di+1 = di +2 (dy – dx). Если di < 0, тогда выбирается Si и yi = yi–1 и di+1 = di +2 dy.

Начальные значения d1 с учетом того, что (x0, y0) = (0, 0)

d1 = 2 dy – dx

Преимуществом алгоритма является то, что для работы алгоритма требу­ются минимальные арифметические воз­можности: сложение, вычитание и сдвиг влево для ум­ножения на 2.

Реализация этого алгоритма на C++ выглядит следующим образом.

 

void MyLine ( int x1, int y1, int x2, int y2, int color )

{

int dx = abs ( x2 - x1 );

int dy = abs ( y2 - y1 );

int sx = x2>=x1?1:-1;

int sy = y2>=y1?1:-1;

if(dy<=dx)

{

int d = ( dy<<1 ) - dx;

int dl=dy<<1;

int d2 = (dy-dx)<<1;

putpixel(x1,y1,color);

for (int x = x1 + sx, y = y1, i = 1; i<= dx;i++,x+=sx)

{

if(d>0)

{

d+= d2;

y+= sy;

}

else d+=dl;

putpixel(x,y,color );

}

}

else

{

int d=(dx<<1) - dy;

int dl=dx<<1;

int d2=(dx-dy)<<1;

putpixel( x1, y1, color );

for ( int x = x1 , y = y1 + sy, i=1 ; i <=dy; i++,y+=sy )

{

if(d>0)

{

d += d2;

x += sx;

}

else

d+=dl;

putpixel ( x, y, color );

}

}

}

Если dy > dx, то необходимо будет использовать этот же алгоритм, но по­шагово увели­чивая y и на каждом шаге вычислять x.