Выбрать главу

// Задаем цветовой ключ для поверхности с курсором

hRet := DDSetColorKey (FDDSImage, RGB(0, 0, 0) ) ;

if Failed (hRet) then ErrorOut(hRet, 'DDSetColorKey');

В качестве первого аргумента указывается имя нужной поверхности. Второй параметр - это тройка чисел, задающих цвет ключа. Все аргументы функции RGB равны нулю, поскольку в этом примере стрелка нарисована на черном фоне (рис. 3.2).

Цвет для ключа задается произвольным, но при рисовании картинки следует помнить, что все, закрашенное этим цветом, не будет отображаться при выводе растра. Растр в примере 24-битный, хоть и используется в нем всего два цвета: черный и синий.

При рисовании вначале с помощью метода BitFast выводим на поверхность заднего буфера фон - предварительно растянутую картинку:

while True do begin

hRet := FDDSBack. BitFast (0, 0, FDDSBackGround, nil, DDBLTFAST_WAIT) ;

if hRet = DDERR_SURFACELOST then begin if Failed (RestoreAll) then Exit;

end

else Break;

end;

Затем в позиции курсора появляется растровое изображение стрелки. Обратите внимание на новую для нас константу в комбинации флагов:

while True do begin

hRet := FDDSBack. BitFast (mouseX, mouseY, FDDSImage, nil,

DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY) ; if hRet = DDERR_SURFACELOST then begin

if Failed (RestoreAll) then Exit; end

else Break;

end;

Добавленная константа заставляет при воспроизведении учитывать цветовой ключ источника. Данный ключ может задаваться для любой поверхности. При блиттинге можно определять, чей цветовой ключ работает - источника или приемника. Чаще всего применяется ключ источника.

Обычным делом для приложений, использующих DirectDraw, является отключение курсора или, как в рассматриваемом примере, замена его пользовательским. С системными черно-белыми курсорами проблем при воспроизведении обычно не возникает, цветные же курсоры могут мерцать или вовсе пропадать.

В описании класса формы добавлен раздел protected, в котором анонсирована процедура-ловушка сообщения, связанного с установкой курсора:

procedure FormSetCursor (var aMsg : TMessage) ; message WM_SETCURSOR;

Код процедуры совсем короткий:

procedure TfrmDD. FormSetCursor (var aMsg : TMessage);

begin

SetCursor (0) ; // He отображать курсор

end;

При перемещении курсора фиксируем его положение в глобальных переменных, следя, чтобы ни один пиксел стрелки не вышел за пределы окна:

procedure TfrmDD. FormMouseMove (Sender : TObject; Shift: TShiftState; X, Y: Integer) ;

begin

if X <= ScreenWidth - 40 then mouseX := X; // Ограничиваем размерами

if Y <= ScreenHeight - 40 then mouseY := Y; // растра стрелки

FormPaint (nil) ; // Вызываем код перерисовки окна

end;

Сам указатель, как видим, никогда не укажет на точку вблизи правой и нижней границы экрана. И есть еще одна серьезная проблема с указателем - если его передвигать быстро, то он может "застыть" далеко от границы окна. Связано это с медленной обработкой событий перемещения мыши, т. к. при быстром передвижении курсора приложение не успевает проследить все его положения. Потом мы займемся этой проблемой основательно.

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

Функция DDReLoadBitmap плохо справляется с перезагрузкой на масштабируемые поверхности, как в случае с фоном этого примера. Минимизируйте, а затем восстановите окно. Растр фона выведется с потерями, на нем появятся квадратики.

Работая с примерами предыдущей главы, вы наверняка заметили, что полноэкранные приложения, использующие DirectDraw, после своей работы оставляют в панели задач след - значок отработавшего приложения. Начиная с этого примера, для устранения такого следа в проектах полноэкранных приложений будем включать обработчик события enclose, содержащий единственную строку с вызовом метода Hide формы.

Еще один важный момент. По завершении работы у объектов, связанных с DirectDraw, перед непосредственно высвобождением памяти будем теперь вызывать метод _Reiease. Такая работа с интерфейсами является более корректной, академичной, но я обязан предупредить, что использование его в некоторых случаях может приводить к исключениям. Проблема плохо понятна, и возникает именно в приложениях, написанных на Delphi. Если вы столкнетесь с ней, то завершайте работу приложения так, как мы это делали раньше.

Обратите внимание, что в случае составной поверхности метод _Reiease вызывается только для первичного буфера, для заднего буфера отдельно этот метод вызывать нет необходимости:

procedure TfrmDD.FormDestroy(Sender: TObject); begin

if Assigned(FDD) then begin

if Assigned(FDDSImage) then begin FDDSImage._Release;

FDDSImage := nil;

end;

if Assigned(FDDSBackGround) then begin FDDSBackGround._Release;

FDDSBackGround := nil;

end;

if Assigned(FDDSPrimary) then begin FDDSPrimary._Release;

FDDSPrimary := nil;

end;

FDD._Release;

FDD := nib;

end;

end;

В знак того, что наши примеры теперь становятся более совершенными, значок приложения устанавливаем отличным от принятого в Delphi по умолчанию, теперь этим значком будет логотип DirectX.

Посмотрим, как использовать цветовой ключ совместно с методом Bit поверхности, для чего переходим к проекту каталога Ех02.

По виду приложение ничем не отличается от предыдущего, изменения коснулись кода воспроизведения, в котором появилась вспомогательная переменная wrkRect типа TRECT:

while True do begin

// Прямоугольник, связанный с пользовательским курсором SetRect (wrkRect, mouseX, mouseY, mouseX + 40, mouseY + 40);

// Используется ключ; добавилась новая константа в комбинации флагов

hRet := FDDSBack.Blt (SwrkRect, FDDSImage, nil,

DDBLT_WAIT or DDBLT_KEYSRC, nil);

if hRet = DDERR_SURFACELOST then begin

if Failed (RestoreAll) then Exit;

end

else Break;

end;

Как видим, для применения цветового ключа потребовалось добавить константу.

Все просто, но для этого метода есть небольшая тонкость. При масштабировании изображения DirectX интерполирует края закрашенных областей, сглаживает переходы между цветами. Так, по крайней мере, происходило у меня. Получается красиво, но при использовании цветового ключа интерполяция может немного подпортить картинку. Установите nil первым аргументом метода Bit и запустите проект. Стрелка растягивается на весь экран, а ее края красиво оттеняются темным оттенком синего. Выглядит симпатично, но, возможно, вы уже почувствовали подвох в том, что чистый синий цвет на границах стрелки потерян. Установите цветовой ключ для поверхности FDDSImage в чистый синий:

hRet := DDSetColorKey (FDDSImage, RGB(0, 0, 255));

if Failed (hRet) then ErrorOut (hRet, 'DDSetColorKey');

И снова запустите проект. Фон будет проглядывать только во внутренних частях стрелки, а не по всему ее силуэту.

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

Выход простой - не использовать подобное масштабирование для растров, на которые предполагается накладывать ключ. В таких случаях нужно осуществлять масштабирование с помощью вспомогательных объектов класса TBitmap, с которыми мы уже сталкивались и сталкнемся не раз.