Читаем О чём не пишут в книгах по Delphi полностью

Каждая "иголка" — это линия длиной FirNeedleLength пикселов, отклоняющаяся от направления прямой на угол FirNeedleAngle градусов. "Иголки" отклоняются попеременно то в одну, то в другую сторону от прямой. В процедуре Line сначала рассчитываются смещения координат конца "иголки" относительно начала и результаты помещаются в глобальные переменные DX1, DY1, DX2, DY2. Переменная Counter служит для определения номера точки. Перед вызовом LineDDA она инициализируется нулем. Затем вызывается функция LineDDA, в качестве одного из параметров которой передается указатель на функцию обратного вызова LineDrawFir. В результате этого функция LineDrawFir будет вызвана последовательно для каждого из пикселов, составляющих линию, начиная с (X1, Y1). LineDrawFir ведет подсчет пикселов, каждый раз увеличивая Counter на единицу. Если остаток от деления номера точки на 10 равен 0, рисуется "иголка", отклоняющаяся в положительном направлении, если 5 — в отрицательном. В остальных случаях не рисуется ничего. Так получается "елочка".

<p>1.3.4.2. "Резиновая" линия и растровые операции</p></span><span>

Теперь нужно дать пользователю возможность рисовать линии. Для этого мы используем стандартную "резиновую" линию: пользователь нажимает левую кнопку мыши и, удерживая ее, передвигает мышь. До тех пор, пока кнопка удерживается, за курсором тянется линия. Как только пользователь отпускает кнопку, линия "впечатывается" в рисунок.

Сама по себе реализация "резиновой" линии очень проста: при наступлении события OnMouseDown запоминаются координаты начала линии и взводится флаг, показывающий, что включен режим рисования "резиновой" линии. Также запоминаются координаты конца отрезка, который на данный момент совпадает с началом. В обработчике OnMouseMove, если включен режим рисования "резиновой" линии, стирается линия со старыми координатами конца и рисуется с новыми. При наступлении OnMouseUp программа выходит из режима рисования "резиновой" линии, рисуя окончательный ее вариант с текущими координатами конца.

Самое сложное в этой последовательности действий — стереть нарисованную ранее линию. Если бы у нас был однородный фон, можно было бы просто нарисовать старую линию еще раз цветом фона — это выглядело бы как ее стирание. Но поскольку фон не однородный, а составлен из нарисованных ранее линий, этот способ мы применить не можем.

Для решения этой задачи мы здесь рассмотрим самый простой метод — инверсное рисование (более сложный метод будет рассмотрен чуть позже). При этом каждая точка, принадлежащая линии, закрашивается не каким-либо фиксированным цветом, а инвертируется (т.е. к текущему цвету точки применяется операция not). Для стирания линии просто рисуем ее еще раз: двойная инверсия восстанавливает предыдущий цвет точек (not not X = X для любого X).

При рисовании пером и кистью GDI позволяет использовать различные растровые операции, которые определяют результирующий цвет каждого пиксела в зависимости от цвета фона и пера или кисти. По умолчанию применяется операция R2_COPYPEN, в которой цвет фона игнорируется, а результирующий цвет пиксела совпадает с цветом пера или кисти. Изменить растровую операцию можно с помощью функции SetROP2 (двойка в названии функции показывает, что устанавливаемая растровая операция имеет два аргумента — цвет рисования и цвет фона: при выводе растровых рисунков могут применяться растровые операции с тремя аргументами — см. функцию BitBlt). Нас будет интересовать операция R2_NOT, которая инвертирует фоновый цвет, игнорируя цвет пера или кисти.

Примечание

Растровая операция влияет на все, что рисуется с помощью пера и кисти, т.е. на рисование границ фигур и их заливку. Кроме того, растровая операция влияет также на результат работы функции SetPixel (и, соответственно, изменение цвета с помощью Canvas.Pixels[X, Y]), т.к. эта операция выполняется с мощью кистей.

Код, рисующий "резиновую" линию, приведен в листинге 1.59.

Листинг 1.59. Рисование "резиновой" линии инверсным методом

procedure TLinesForm.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);

begin

 if Button = mbLeft then begin

  OldX := X;

  OldY := Y;

  BegX := X;

  BegY := Y;

  LineDrawing := True;

 end;

end;

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже