Графика DirectX в Delphi
Шрифт:
Сразу после создания первичной поверхности формируется этот объект, ответственный за отсечение области вывода, и присоединяется к области вывода:
// Создание объекта отсечения
hRet := FDD.CreateClipper(0, FDDCiipper, nil);
if Failed (hRet) then ErrorOut(hRet, 'CreateClipper FAILED');
// Определяем окно, связанное с отсечением области вывода
hRet := FDDCiipper.SetHWnd(0, Handle);
if Failed (hRet) then ErrorOut(hRet, 'SetHWnd FAILED');
// Устанавливаем объект отсечения для первичной поверхности
hRet := FDDSPrimary.SetClipper(FDDClipper);
if Failed (hRet) then ErrorOut(hRet, 'SetClipper FAILED^);
Можете
Одно небольшое замечание. В программах DirectX SDK можно обнаружить, что объект отсечения не удаляется по окончании работы, я же делаю это в моих примерах намеренно. Легко проверить это: объект, связанный с отсечением, имеет по окончании работы значение, отличное от nil, а в таком случае лучше будет явным образом освобождать память, занятую им. Также иногда можно встретить, что эта переменная присваивается nil сразу после присоединения к первичной поверхности.
Важно подчеркнуть, что при использовании отсечения нельзя применять для вывода на первичную поверхность метод BitFast.
Следующий пример, проект каталога Ех27, продолжает тему оконных приложений, отличается он от предыдущего пользовательским курсором (рис. 3.13).
Буферизацию приходится организовывать самостоятельно, для экономии памяти вспомогательная поверхность создается при каждом изменении размеров окна:
procedure TfrmDD.FormResize(Sender: TObject);
var
hRet : HRESULT;
ddsd : TDDSurfaceDesc2;
begin
if Assigned(FDDSBack) then FDDSBack := nil;
ZeroMemory(@ddsd, SizeOf(ddsd));
with ddsd do begin
dwSize := SizeOf(ddsd);
dwFlags := DDSD_CAPS or DDSDJiEIGHT or DDSD_WIDTH;
ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN;
dwWidth := ClientWidth; // Размеры совпадают с текущими размерами
dwHeight := ClientHeight; // окна
end;
hRet := FDD.CreateSurface(ddsd, FDDSBack, nil);
if Failed(hRet) then ErrorOut(hRet, 'Create Back Surface');
FormPaint (nil);
end;
Обратите внимание, что в этом примере пользовательским курсором можно указать на любую точку клиентской области окна. Для отсечения нужной части поверхности образа (ее размер 32x32 пиксела) объявлена переменная rcMouse типа TRECT. При перемещении курсора вблизи границы окна оставляем для воспроизведения только часть образа:
procedure TfrmDD.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
wrkl, wrkJ : Integer;
begin
mouseX := X;
if X < ClientWidth - 32
then wrkl := 32 //
else wrkl := ClientWidth - X; // Воспроизводить только часть образа
mouseY := Y;
if Y < ClientHeight - 32
then wrkJ := 32 // По Y помещается весь растр
else wrkJ := ClientHeight - Y; // Воспроизводить только часть образа
SetRect (rcMouse, 0, 0, wrkl, wrkJ); // Итоговый прямоугольник образа
FormPaint (nil); // Принудительно перерисовываем окно
end;
При перерисовке окна метод BitFast приходится использовать только для вывода растрового изображения курсора:
procedure TfrmDD.FormPaint(Sender: TObject);
var
rcDest, wrkRect : TRECT;
p : TPOINT;
begin
p.X := 0;
p.Y := 0;
Windows.ClientToScreen(Handle, p);
Windows.GetClientRect(Handle, rcDest);
OffsetRect(rcDest, p.X, p.Y);
SetRect (wrkRect, 0, 0, ClientWidth, ClientHeight);
//На вспомогательную поверхность помещаем растровое изображение фона
if Failed (FDDSBack.Blt (SwrkRect, FDDSBackGround, nil,
DDBLT^WAIT, nil}) then if Failed (RestoreAll) then Exit;
// Поверх фона размещаем растровое изображение курсора
if Failed (FDDSBack.BltFast (mouseX, mouseY, FDDSImage, @rcMouse,
DDBLTFAST_WAIT or DDBLTFAST_SRCCOLORKEY))
then if Failed (RestoreAll) then Exit;
// Копируем содержимое вспомогательной поверхности на первичную
if Failed (FDDSPrimary.Blt (@rcDest, FDDSBack, nil, DDBLT__WAIT, nil))
then if Failed (RestoreAll) then Exit;
end;
Для отключения отображения курсора в этом примере прибегнем к альтернативному способу: воспользуемся процедурой showcursor. В начале работы вызовем ее с аргументом False. Однако с курсором осталась связанной одна проблема, возникающая при нахождении его в области заголовка и в пределах рамки окна, когда пользовательский курсор мы отобразить уже не можем, а системный отключен. Полного решения данной проблемы достичь нелегко, если включать курсор в ловушке сообщения WM_NCMOUSEMOVE, возникающего при нахождении курсора за пределами клиентской части окна, то результат может получиться неустойчивым, придется все равно отслеживать возвращение курсора в окно.
Самое простое решение - управлять видимостью курсора в обработчике OnMouseMove, включать его при нахождении курсора вблизи границ окна. Но для хорошего функционирования алгоритма надо либо беспрерывно перерисовывать окно, либо добиться более высокой скорости работы с мышью.
Вскользь я уже говорил, что для поверхностей можно принудительно устанавливать одинаковый формат пиксела. Посмотрим на примере проекта каталога Ех28, как это сделать. Здесь введена переменная Pixel Format типа TDDPixelFormat; после создания первичной поверхности заносим ее формат в данную переменную: