Поскольку программа является растровым (точечным) редактором, то изображение находится в виде объекта графического класса TBitmap и работа всех эффектов основана на работе с этим классом. TBitmap содержит двумерный массив, в каждой ячейке которого записан определённый цвет. То есть мы имеем дело с двумерной (плоскостной) системой координат: каждая точка рисунка описывается двумя координатами, например (45, 87).
Доступ к определённой точке можно получить двумя способами: через свойство Canvas.Pixels[x, y] (x и y – координаты точки), которое имеет тип TColor, или с помощью свойства ScanLine[y], которое возвращает указатель на начало строки под номером 'y'. Первый способ работает гораздо медленнее второго, поэтому он почти не используется, хотя и более удобен.
Каждый цвет (в компьютерном представлении) состоит из трёх оттенков: красного (red), зелёного (green) и синего (blue) – RGB. При этом каждый из оттенков может принимать значения от 0 до 255, то есть, всего компьютер (монитор) может отображать 256*256*256=16777216 разных цветов.
Все функции и процедуры, имеющие отношение к созданию эффектов, находятся в файле FeProcs.pas, который занимает 69 КБ, так как этих функций очень много.
Рассмотрим пример реализации эффекта 'Негатив':
Type
TRGB = record
B, G, R: Byte;
end;
pRGB = ^TRGB;
procedure InvertBitmap(Bitmap: TBitmap);
var x, y: Integer;
Dest: pRGB;
Begin
for y := 0 to Bitmap.Height - 1 do begin
Dest := Bitmap.ScanLine[y]; //помещаем указатель в начало строки y
for x := 0 to Bitmap.Width - 1 do begin
with Dest^ do begin
R := 255 - R; //чтобы обратить цвет,
G := 255 - G; //нужно обратить на противоположные
B := 255 - B; //его составляющие
end;
Inc(Dest); //перемещаем указатель вправо
end;
end;
end;
Цикл начинается с 0 и заканчивается на Bitmap.Height – 1 (Bitmap.Width – 1), так как в системе координат Windows отсчёт начинается с точки (0, 0), а точки (Width – ширина, Height – высота) не существует. Внешний цикл проходит по порядку все строки (координата Y), потомучто свойство Bitmap ScanLine передаёт указатель на строку, а не на столбец.
Интересной (с точки зрения программирования) является функция подсчёта цветов рисунка:
function HowManyColors(Bitmap: TBitmap): Integer;
var i: Byte;
x, y: Integer;
Dest: pRGB;
RGBArray: array[Byte, Byte] of array of Byte;
Begin
Result := 0;
for y := 0 to Bitmap.Height - 1 do begin
Dest := Bitmap.ScanLine[y];
for x := 0 to Bitmap.Width - 1 do begin
with Dest^ do
if RGBArray[r, g] <> nil then
for i := 0 to High(RGBArray[r, g]) do begin
if RGBArray[r, g] [i] = b then Break; //такой цвет уже есть – выход из цикла
if i = High(RGBArray[r, g]) then begin//если это последний 'круг' цикла, то
Изначально, для решения задачи подсчёта цветов применялся следующий алгоритм: использовалась глобальная переменная, которая являлась трёхмерным массивом - «ColorsArray: array [0..255, 0..255, 0..255] of Boolean», то есть имел для каждого существующего цвета отдельную ячейку типа Boolean – всего 16777216 ячеек; в начале функции значения всех ячеек устанавливались в False (ложный), это означало, что нет ни одного цвета; затем цвет каждой точки разлагался на составляющие R, G и B, и проверялось значение соответствующей ячейки - «ColorsArray[R, G, B]», если оно было ложным, то изменялось на True (истинный), а количество цветов рисунка увеличивалось на один. Но, естественно, такой подход крайне иррационален, так как компилятор не позволяет выделять так много памяти внутри процедуры, а выделение 16 Мбайт памяти при загрузке тормозит не только саму программу, но и другие процессы системы. По этой причине пришлось отказаться от такого решения и разработать новый алгоритм, что и было сделано. Функция, реализующая новый подход изложена выше, а сам он заключается в динамическом выделении памяти. Переменная RGBArray – это двумерный массив, каждая ячейка которого, в свою очередь, является динамическим массивом типа Byte (0..255). Фактически изначально вообще не выделяется памяти, она выделяется по мере увеличения числа цветов.