DirectX Графика в проектах Delphi

       

Оригинальный сплэш



Так в обиходе программисты называют заставки, появляющиеся в начале работы приложения (splash - красочное пятно, всплеск). Выводят их на время долгой инициализации основного модуля, как правило, для того, чтобы скрасить секунды (или минуты) ожидания пользователя. Иногда их использованием авторы преследуют цель поразить воображение зрителя, заявить на весь мир о своих выдающихся художественных и профессиональных способностях.
В этом разделе я приведу пример, который может стать основой для создания вашей собственной оригинальной заставки. Это проект каталога Ex01. Во время его работы посередине рабочего стола выводится изображение земного шара, на фоне которого вращается фраза "DirectX". На неискушенных пользователей окна непрямоугольной формы обычно производят сильное впечатление. Подобные окна можно создавать разными способами, например, с помощью регионов. Мы же решим задачу обычным для DirectDraw способом. Совсем необязательно должно получаться именно круглое окно, как в моем примере.
Приемы, используемые в проекте, во многом вам знакомы по примерам предыдущих глав, однако добавилось и кое-что новое.
Заставка должна появляться всегда посередине экрана, при любых установленных разрешениях, поэтому в начале работы нам необходимо определить текущие размеры рабочего стола, относительно середины которого выверить координаты вывода нашего образа размером 256x256 пикселов:

HalfWidth := (GetSystemMetrics (SM_CXSCREEN) -256) div2;
HalfHeight := (GetSystemMetrics(SM_CYSCREEN) - 256) div 2;

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

Уровень кооперации устанавливается нормальным, а очередной кадр не выходит за границу предыдущего. Поэтому наша заставка эффектно располагается поверх всех окон, и нет необходимости производить манипуляций с ее фоном (запоминать и восстанавливать для каждого кадра).
Но для того, чтобы заставка не исчезла при изменениях на рабочем столе, ее необходимо постоянно перерисовывать. Чтобы перерисовка протекала с большим эффектом, работают с двумя образами: земного шара и вращающейся надписи.
Мы уже использовали прием с вращением изображения, основанный на непосредственном доступе к 8-битной поверхности. Пример этой главы рассчитан на, минимум, 16-разрядную глубину поверхности, а вызываемая нами тогда функция вращения для такого режима требует корректировки.
Я переписал эту функцию. Теперь поворачивается содержимое передаваемого объекта класса TBitmap, и возвращается объект такого же класса:


function TfrmDD.RotateBmp (const BitmapOriginal: TBitmap;
const iRotationAxis, jRotationAxis: Integer;
const AngleOfRotation: Single): TBitmap;
const
MaxPixelCount = 32768;
type
TRGBTripleArray = Array [0..MaxPixelCount-1] of TRGBTriple;
pRGBTripleArray = ATRGBTripleArray;
var
cosTheta Single;
i : Integer;
iOriginal : Integer;
iPrime : Integer;
j Integer;
jOriginal : Integer;
jPrime : Integer;
RowOriginal : pRGBTripleArray;
RowRotated : pRGBTRipieArray;
sinTheta : Single;
begin
Result := TBitmap.Create; // Создание результирующего растра
Result.Width := BitmapOriginal.Widths
Result .Height := BitmapOriginal.Height;
Result.PixelFormat := pf24bit; // Очень важно задать явно режим пиксела
sinTheta := sin (AngleOfRotation);
cosTheta := cos (AngleOfRotation);
// Расчет источника для пикселов повернутого растра
for j := Result.Height - 1 downto 0 do begin
RowRotated := Result.Scanline[j];
jPrime := j - JRotationAxis;
for i := Result.Width-1 downto 0 do begin
iPrime := i - iRotationAxis;
iOriginal := iRotationAxis + round(iPrime * CosTheta - jPrime *
sinTheta);
jOriginal := JRotationAxis + round(iPrime * sinTheta + jPrime *
cosTheta);
if (iOriginal >= 0) and (iOriginal <= BitmapOriginal.Width-1) and
(jOriginal >= 0) and (jOriginal <= BitmapOriginal.Height-1)
then begin
RowOriginal := BitmapOriginal.Scanline[jOriginal];
RowRotated[i] := RowOriginal[iOriginal]
end
else begin // "Новые" пикселы заполняются черным, цветом ключа
RowRotated[i].rgbtBlue := 0;
RowRotated[i].rgbtGreen := 0;
RowRotated[i].rgbtRed := 0
end
end
end;
end;

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

function TfrmDD.UpdateFrame : HRESULT;
begin
// Повернутый растр копируем на поверхность
FDDSLogo with RotateBmp (wrkBitmap, 128, 128, Angle) do begin
DDCopyBitmap (FDDSLogo, Handle, 0, 0, Width, Height);
Free end;
Angle := Angle - 0.1;
// Наращиваем угол поворота
if Angle > - 2 * Pi then Angle := Angle + 2 * Pi;
// Теоретически возможные ошибки блиттинга игнорируем
// На заднем буфере подготавливаем итоговую картинку
FDDSBack.BltFast(О, О, FDDSImage, nil, DDBLTFAST_WAIT or
DDBLTFAST_SRCCOLORKEY); // Вывод фона, земной шар
FDDSBack.BltFast(О, О, FDDSLogo, nil, DDBLTFAST_WAIT or
DDBLTFAST_SRCCOLORKEY); // На фон накладываем повернутую надпись
// Вывод посередине экрана заставки
Result := FDDSPrimary.BitFast(HalfWidth, HalfHeight, FDDSBack,
nil, DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY);
end;

Перед окончанием работы заставку необходимо убрать с экрана. Обращаю внимание, как это делается: появился обработчик события onclose, в котором собственно окно приложения занимает всю область заставки:

procedure TfrmDD.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Left := HalfWidth;
Top := HalfHeight;
Width := 256;
Height := 256;
end;


Содержание раздела