//------------------------------------------------------------------
//    Файл:     ddutils.pas
//    Описание: Сервисные функции для DirectDraw API
//------------------------------------------------------------------
unit ddutils;

interface

uses
  windows,
  directdraw;




//------------------------------------------------------------------
// прототипы функций
//------------------------------------------------------------------
function CreateSurface( var pSurface: IDirectDrawSurface7;
                        dwWidth, dwHeight: DWORD ): boolean;

function CopyBitMap( var pSurface: IDirectDrawSurface7; const hBmp: HBITMAP;
                     pWidth, pHeight: PDWORD ): boolean;

function LoadBitMap( name: pchar; pbi: PBITMAPINFO ): pointer;

implementation




//------------------------------------------------------------------
// создаёт поверхность DirectDraw нужных размеров
//------------------------------------------------------------------
function CreateSurface( var pSurface: IDirectDrawSurface7; dwWidth, dwHeight: DWORD ): boolean;
var
  ddsd2: TDDSURFACEDESC2;
  pDD7:  IDirectDraw7;
begin
  result := false;

  // чтобы избежать передачу интерфейса IDirectDraw7 через параметр функции,
  // просто на короткое время создадим собственный
  if FAILED( DirectDrawCreateEx( nil, pDD7, IID_IDirectDraw7, nil ) ) then
     exit;

  // Устанавливаем уровень кооперации. Фактически, если в программе до этого уже
  // был создан объект DirectDraw и для него установлен эксклюзивный режим
  // кооперации, единственным режимом должен быть DDSCL_NORMAL
  if FAILED( pDD7.SetCooperativeLevel( 0, DDSCL_NORMAL ) ) then
     exit;

  // если поверхность существует, то удаляем
  if pSurface <> nil then
     pSurface := nil;

  // подготавливаем структуру TDDSURFACEDESC2
  ZeroMemory( @ddsd2, sizeof( TDDSURFACEDESC2 ) );
  ddsd2.dwSize := sizeof( TDDSURFACEDESC2 );

  // заполняем поля структуры
  ddsd2.dwFlags        := DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT;
  ddsd2.ddsCaps.dwCaps := DDSCAPS_OFFSCREENPLAIN;
  ddsd2.dwWidth        := dwWidth;
  ddsd2.dwHeight       := dwHeight;

  // создаём поверхность
  if FAILED( pDD7.CreateSurface( ddsd2, pSurface, nil ) ) then
  begin
    pDD7 := nil;
    exit;
  end;

  // удаляем локальный объект DirectDraw
  pDD7 := nil;

  result := true;
end;




//------------------------------------------------------------------
// копирует растр на поверхность DirectDraw
//------------------------------------------------------------------
function CopyBitMap( var pSurface: IDirectDrawSurface7; const hBmp: HBITMAP;
                     pWidth, pHeight: PDWORD ): boolean;
var
  destDC: HDC;    // поверхность-приёмник
  srcDC:  HDC;    // поверхность-источник
  bm:     BITMAP; // BITMAP из windows.pas
begin
  result := false;

  // создаём контекст памяти
  srcDC := CreateCompatibleDC( 0 );
  if srcDC = 0 then
     exit;

  // выбираем в контекст растр
  SelectObject( srcDC, hBmp );

  // получаем размеры растра, данные заполняют структуру BITMAP
  if GetObject( hBmp, sizeof( bm ), @bm ) = 0 then
     exit;

  // создаём поверхность с размерами растра
  if not CreateSurface( pSurface, bm.bmWidth, bm.bmHeight ) then
  begin
    DeleteObject( srcDC );
    exit;
  end;

  // возвращаем через параметры функции размер созданного изображения
  pWidth^ := bm.bmWidth;
  pHeight^ := bm.bmHeight;

  // создаём GDI-совместимый контекст для поверхности
  if FAILED( pSurface.GetDC( destDC ) ) then
  begin
    DeleteObject( srcDC );
    exit;
  end;

  // копируем растр на поверхность DirectDraw функцией GDI BitBlt()
  BitBlt( destDC, 0, 0, bm.bmWidth, bm.bmHeight, srcDC, 0, 0, SRCCOPY );

  // удаляем контексты из памяти
  pSurface.ReleaseDC( destDC );
  DeleteDC( srcDC );

  result := true;
end;




//------------------------------------------------------------------
// загружает 24-битный растр и возвращает указатель на данные
//------------------------------------------------------------------
function LoadBitMap( name: pchar; pbi: PBITMAPINFO ): pointer;
var
  dwDIBSize:   DWORD;             // размер графических данных

  bfh:         BITMAPFILEHEADER;  // заголовки
  bi:          BITMAPINFO;        // bmp-файла

  hFile:       THandle;           // дескриптор открываемого файла
  dwBytesRead: DWORD;             // количество реально прочитанных байт
begin
  result := nil;

  // открываем указанный файл и получаем его дескриптор
  hFile := CreateFile( name, GENERIC_READ, FILE_SHARE_READ, nil,
                       OPEN_EXISTING, 0, 0 );
  if hFile = INVALID_HANDLE_VALUE then
     exit;

  // считываем структуру BITMAPFILEHEADER
  ReadFile( hFile, bfh, sizeof( BITMAPFILEHEADER ), dwBytesRead, nil );
  // проверяем тип файла (для корректности)
  if bfh.bfType <> $4d42 then
  begin
    CloseHandle( hFile );
    exit;
  end;

  // считываем структуру BITMAPINFOHEADER
  ReadFile( hFile, bi, sizeof( BITMAPINFOHEADER ), dwBytesRead, nil );
  // проверяем, является ли растр 24-битным
  if bi.bmiHeader.biBitCount <> 24 then
  begin
    CloseHandle( hFile );
    exit;
  end;

  // определяем размер DIB
  dwDIBSize := GetFileSize( hFile, nil ) - sizeof( BITMAPFILEHEADER ) - sizeof( BITMAPINFOHEADER );
  // выделяем участок памяти
  result := pbyte(GlobalAllocPtr( GMEM_MOVEABLE, dwDIBSize ));
  // читаем данные DIB
  ReadFile( hFile, result^, dwDIBSize, dwBytesRead, nil );
  // закрываем файл
  CloseHandle( hFile );

  // передаём через интерфейс функции структуру BITMAPINFO
  if pbi <> nil then
     pbi^ := bi;
end;

end.
