Работаем с промежутками(анг. Span).
Что такое промежуток? Это узкоспециализированный термин в графике. Он означает горизонтальные участки образовавшиеся после нарезки многоугольника на горизонтальные полоски.
Вовремя нарезки на одной линии могут образоваться несколько таких промежутков. Промежуток располагается между гранями и состоит из скосов и сплошных лент.
Введем два типа промежутков. Сжатые и несжатые. Каждый несжатые промежуток описывает скосы и содержит массив весовых коэффициентов. Весовой коэффициент соответствует проценту заполнения пикселя многоугольником. Где идет грань он же скос то там коэффициенты уменьшается или увеличивается. А где идет сплошная лента то там коэффициенты постоянные. Для таких и исполняется сжатие.
Небольшое отступление от темы:
Зачем нужно два типа? Дело в том что основной операция при рисовании является закраска пикселей. И тут важна оптимизация.
Можно выделить 3 функции закраски.
1) Простое присвоение
2) Присвоение при смешивание с постоянным коэффициентом
3) Присвоение со смешивание с разными коэффициентами.
Самая быстрая 1 потом 2 и самая медленная 3 функция. Скорость определяет число операций.
1) Запись.
2) Смешивание запись
3) Чтение смешивание запись.
Более детально я расскажу когда буду рассказывать про закраску и смешивание.
Вернемся к промежуткам.
Вначале я хотел сделать по простому 2 функции.
Код:
procedure AddСoSpan(Y, x0,x1:Integer);
begin
// Span Clipping
if X0<0 then X0:=0;
if X1<0 then X1:=0;
if X0>=Width then X0:=Width;
if X1>=Width then X1:=Width;
//
Span.Kind:=skCompressed;
Span.Y:=Y;
Span.X:=X0;
Span.N_1:=(X1-X0)-1;
Spans[SpansCount]:=Span;
Inc(SpansCount);
if SpansCount>=Length(Spans) then SetLength(Spans,SpansCount*2);
end;
procedure AddUSpan(Y, x0,x1:Integer; AAWeight:PByte);
begin
// Span Clipping
if X0<0 then X0:=0;
if X1<0 then X1:=0;
if X0>=Width then X0:=Width;
if X1>=Width then X1:=Width;
//
Span.Kind:=skUnCompressed;
Span.Y:=Y;
Span.X:=X0;
Span.N_1:=(X1-X0)-1;
Span.BufWeight:=AAWeight;
Spans[SpansCount]:=Span;
Inc(SpansCount);
if SpansCount>=Length(Spans) then SetLength(Spans,SpansCount*2);
end;
Но потом все таки решил отказаться от такого подхода. И разбить на 3 функции, как советуют Макконал в своей книге совершенный код. 1) Вернуться к предыдущему варианту я всегда успею. 2) Кадрирование требует перемещения указателя по весовому буферу. Что усложняет код. Да и по сути надо будет создать новый промежуток и заменить старый, а для этого нужен код для создания промежутка.
3) группировка однородных операций всегда работает быстрее. Поэтому отсечение лучше сделать отдельным проходом.
Код:
procedure AddSpan(const Span:PSpan);
begin
Spans[SpansCount]:=Span;
Inc(SpansCount);
if SpansCount>=Length(Spans) then SetLength(Spans,SpansCount*2);
end;
Отделение добавления промежутка в список промежутков позволяет скрыть информацию об реализации. В будущем это позволит изменять реализацию. Тем более управления памятью и структура коллекции ещё не проработана. А в таком случае изменения затронут минимум кода.
Код:
function CoSpan(Y, X, Width:Integer; Weight:Byte):TSpan;
begin
Result.Kind:=skCompressed;
Result.Y:=Y;
Result.X:=X;
Result.Width:=Width;
Result.UniformWeight:=Weight;
Result.WeightsCount:=0;
Result.Weights:=Nil;
end;
function USpan(Y, X, Width:Integer; PWeights:PByte):TSpan;
begin
Result.Kind:=skUnCompressed;
Result.Y:=Y;
Result.X:=X;
Result.Width:=Width;
Result.WeightsCount:=Width;
Result.Weights:=pointer(PWeights);
end;
По поводу выбора названий.
СoSpan сокращение от
Compressed
Span.
USpan сокращение от
UnCompressed
SpanN_1 длина минус один. Небольшой трюк для того что-бы втолковать оптимизатору что от него нужно.
Weight - почему ширина, а не x0,x1?
Дело в том что x0<x1, а может и наоборот x1<x0.
А длина бывает положительной и отрицательной. А вот ширина в большинстве случаев число положительное. При реализации закраски не хотелось бы иметь лишнего ветвления. А внутри функции сортировку тоже не хочется делать так как при разбиении на промежутки идет сортировка активных граней по Х. И повторная сортировка будет лишней. По этим причинам используется Weight. А вот тип знаковый, отрицательная ширина не рисуется.
UniformWeight - одинаковый, однородный вес. Используется в сжатом промежутке.
Weights - весовые коэффициенты.
PWeights - приставка P означает передачу указателя.
А вот с типом весового коэффициента пока не определился. Дело в том что для закраски нужна оптимизация надо использовать числа с фиксированной запятой. Но с другой стороны они могут быть и с плавающей запятой.