//DirectDraw.CPP 
#include <math.h>
#include "main.h"
#include "DirectDraw.h"
//------------------------------------------------------
DWORD TRANSCOLOR;//Переменная задания прозрачного цвета
WORD BIT_MODE=16;//Переменная задания битового режима
//------------------------------------------------------
//переменные для функции CreateSurface(int x, int y)
SurfaceLocation fSurfLoc=BOTH;
//------------------------------------------------------
LPDIRECTDRAW7 pDD;
LPDIRECTDRAWPALETTE pDDPal;
LPDIRECTDRAWPALETTE pDirectDrawPal;
PALETTEENTRY palEntries[256];
LPDDSURFACEDESC2 lpDDSurfaceDesc;
//------------------------------------------------------
//первичная поверхность
LPDIRECTDRAWSURFACE7 pPrimarySurface;
//вторичная поверхность
LPDIRECTDRAWSURFACE7 pBackBuffer;
//своя вторичная поверхность
LPDIRECTDRAWSURFACE7 buffer;
//маленький буфер
LPDIRECTDRAWSURFACE7 buf;
//Массив поверхностей
LPDIRECTDRAWSURFACE7 image[image_max];
//Массив имен файлов растров
char* files[image_max];
//счетчик поверхностей	
int image_num=0;
//------------------------------------------------------
//Функция создает первичный объект DirectDrawSurface
//с главными поверхностями
bool CreateMainSurfaces()
{
	//Объявление необходимых для многих функций DirectDraw
	//структур и переменных
	DDSURFACEDESC2 ddSurfaceDesc;
	DDSCAPS2 ddsCaps;
	HRESULT hRet;

	//Отчистка структуры от "мусора" и установка поля ее размера
	ZeroMemory(&ddSurfaceDesc, sizeof(ddSurfaceDesc));
	ddSurfaceDesc.dwSize=sizeof(ddSurfaceDesc);

	//Установка необходимых полей структуры
	ddSurfaceDesc.dwFlags=DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
	ddSurfaceDesc.ddsCaps.dwCaps=DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
	ddSurfaceDesc.dwBackBufferCount=1;

	//Создание поверхности
	hRet=pDD->CreateSurface(&ddSurfaceDesc,	&pPrimarySurface, NULL);
	if (hRet!=DD_OK)
		return (false);
	//Создание вторичного буфера
	ZeroMemory(&ddsCaps, sizeof(ddsCaps));
	ddsCaps.dwCaps=DDSCAPS_BACKBUFFER;
	hRet=pPrimarySurface->GetAttachedSurface(&ddsCaps,&pBackBuffer);
	if(hRet!=DD_OK)
		return (false);

	//Создать свой вторичный буфер
	buffer=CreateSurface(SizeWindowX,SizeWindowY);
	if(buffer==NULL)return (false);

	//буфер для определения цвета
	buf=CreateSurface(16,16);
	if(buf==NULL)return(false);
	return true;
}
//Инициализация DirectDraw
bool InitDirectDraw (HWND hwnd,int color_depth)
{
	//Переменная для возвращаемых кодов
	HRESULT hRet;
	//Обнуляем все интерфейсы
	pPrimarySurface=NULL;
	pBackBuffer=NULL;
	buffer=NULL;
	pDDPal=NULL;
	//Создание интерфейса IDirectDraw7
	hRet=DirectDrawCreateEx(NULL, (VOID**)&pDD, IID_IDirectDraw7, NULL);
	if (hRet!=DD_OK)
	{
		ErrorHandle(hwnd, "DirectDrawCreate");
		return (false);
	}
	//Установка эксклюзивного режима кооперации
	hRet=pDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
	if (hRet!=DD_OK)
	{
		ErrorHandle(hwnd,"SetCooperativeLevel");
		return (false);
	}
	//только 16 и 32 битные режимы, если другое значение, то 32 битный режим
	if(color_depth==8||color_depth==16||color_depth==32);
	else color_depth=16;//значение цветового режима по умолчанию
	BIT_MODE=color_depth;//объявить цветовой режим
	//Установление прозрачного цвета в зависимости от битового режима
	if(BIT_MODE==8)TRANSCOLOR=254;//задание прозрачного цвета для 8-битного режима
	if(BIT_MODE==16)TRANSCOLOR=_RGB16BIT565(255,0,255);//задание прозрачного цвета для 16-битного режима
	if(BIT_MODE==32)TRANSCOLOR= _RGB32BIT(0,255,0,255);//задание прозрачного цвета для 32-битного режима
	//Установка необходимого видеорежима
	hRet=pDD->SetDisplayMode(SizeWindowX, SizeWindowY, BIT_MODE, NULL, NULL);
	if (hRet!=DD_OK)
	{
		ErrorHandle(hwnd,"SetDisplayMode");
		return (false);
	}

	//Создание главных поверхностей
	if (!CreateMainSurfaces())
	{
		ErrorHandle(hwnd, "CreateMainSurfaces");
		return (false);
	}
	//Если цветовой режим 8 бит - создать палитру
	if(BIT_MODE==8)
	{
		//Создание палитры DirectDraw
		pDDPal=CreateDirectDrawPalette(pDD,"data\\palette.bmp");
		if (pDDPal==NULL)
		{
			ErrorHandle(hWnd, "CreateDirectDrawPalette");
			return (false);
		}
		//Присваивание палитры первичной поверхности
		pPrimarySurface->SetPalette(pDDPal);
	}
	return (true);
}
//Очистка всех интерфейсов, связанных с DirectDraw
void RemoveDirectDraw()
{
	//Проверяем, существует ли интерфейс IDirectDraw7
	if (pDD != NULL)
	{
		//Проверяем, существует ли интерфейс первичной поверхности
		if (pPrimarySurface!=NULL)
		{
			//Уничтожаем интерфейс первичной поверхности
			pPrimarySurface->Release();
			pPrimarySurface = NULL;
		}
		//Проверка на существование внеэкранной поверхности
		if (buffer!=NULL)
		{
			//Уничтожение буфера
			buffer->Release();
			buffer=NULL;
		}
		//Проверка на существование интерфейса палитры
		if(pDDPal!=NULL)
		{
			//Уничтожение интерфейса палитры
			pDDPal->Release();
			pDDPal=NULL;
		}
		//Уничтожение поверхностей
		for(int i=0;i<image_num;i++)
		{
			if(image[i]!=NULL)
			{
				image[i]->Release();
				image[i]=NULL;
			}
		}
		if(buf!=NULL)
		{
			buf->Release();
			buf=NULL;
		}
		//Уничтожение интерфейса DirectDraw
		pDD->Release();
		pDD=NULL;

	}
}
//проверка на потерю поверхности с востановлением
bool CheckLost()
{
	//предотвращение ошибочной проверки
	if(pPrimarySurface==NULL)return(false);
	//Проверка на "потерю" первичной поверхности
	if(pPrimarySurface->IsLost())
	{
		//Восстановление первичной поверхности и вторичного буфера
		pPrimarySurface->Restore();
		if(buffer->IsLost())
			buffer->Restore();
	}return(true);
}
//---------------------------------------------------------
//Создание палитры
LPDIRECTDRAWPALETTE CreateDirectDrawPalette(LPDIRECTDRAW7 pDD,char* filename)
{
	//Проверка на существование интерфейса палитры
	if(pDDPal!=NULL)
	{
		//Уничтожение интерфейса палитры
		pDDPal->Release();
		pDDPal=NULL;
	}
	//Объявление интерфейсов и структур для работы с палитрой
	HRESULT hRet;
	LPRGBQUAD pColorTable;
	UINT uMemNeed=sizeof(RGBQUAD)*256;
	DWORD nBytesRead;
	//Открытие графического файла, содержащего палитру
	HANDLE hFile=CreateFile(filename, GENERIC_READ,
			FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	if (hFile==INVALID_HANDLE_VALUE)
	{
		pDirectDrawPal=NULL;
		return (pDirectDrawPal);
	}
	//Выделение памяти под файловую палитру
	pColorTable = (LPRGBQUAD)malloc(uMemNeed);
	//Установка указателя файла на начало палитры
	SetFilePointer(hFile, sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER), NULL, FILE_BEGIN);
	//Чтение палитры из файла
	ReadFile(hFile, (LPVOID)pColorTable, uMemNeed, &nBytesRead, NULL);
	//Закрытие графического файла
	CloseHandle(hFile);
	//Перевод палитры из RGBQUAD в RGBTRIPPLE
	for (int x=0;x<256;x++)
	{
		palEntries[x].peRed=pColorTable[x].rgbRed;
		palEntries[x].peBlue=pColorTable[x].rgbBlue;
		palEntries[x].peGreen=pColorTable[x].rgbGreen;
	}
	//Создание палитры DirectDraw
	hRet=pDD->CreatePalette(DDPCAPS_8BIT | DDPCAPS_ALLOW256,
		palEntries, &pDirectDrawPal, NULL);
	if (hRet!=DD_OK)
		pDirectDrawPal=NULL;
	//Освобождение памяти
	free(pColorTable);
	return (pDirectDrawPal);
}
//------------------------------------------------------
// Создаёт поверхность с выбранной шириной и высотой
//------------------------------------------------------
LPDIRECTDRAWSURFACE7 CreateSurface(int x, int y)
{
	LPDIRECTDRAWSURFACE7 pDDSurface;
	DDSURFACEDESC2 ddSurfaceDesc;
	HRESULT hRet;
	
	if ((fSurfLoc==BOTH) || (fSurfLoc==VIDEO))
	{
		ZeroMemory(&ddSurfaceDesc,sizeof(ddSurfaceDesc));
		ddSurfaceDesc.dwSize=sizeof(ddSurfaceDesc);
		ddSurfaceDesc.dwFlags=DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
		ddSurfaceDesc.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN|DDSCAPS_VIDEOMEMORY;
		ddSurfaceDesc.dwWidth=x;
		ddSurfaceDesc.dwHeight=y;
		
		hRet=pDD->CreateSurface(&ddSurfaceDesc,&pDDSurface,NULL);
		if(SUCCEEDED(hRet))
			return (pDDSurface);
		else if (fSurfLoc==VIDEO)
			return (NULL);
	}

	if ((fSurfLoc==BOTH) || (fSurfLoc==SYSTEM))
	{
		ZeroMemory(&ddSurfaceDesc,sizeof(ddSurfaceDesc));
		ddSurfaceDesc.dwSize=sizeof(ddSurfaceDesc);
		ddSurfaceDesc.dwFlags=DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH;
		ddSurfaceDesc.ddsCaps.dwCaps=DDSCAPS_OFFSCREENPLAIN|DDSCAPS_SYSTEMMEMORY;
		ddSurfaceDesc.dwWidth=x;
		ddSurfaceDesc.dwHeight=y;

		hRet=pDD->CreateSurface(&ddSurfaceDesc,&pDDSurface,NULL);
		if(SUCCEEDED(hRet))
			return (pDDSurface);
		else if (fSurfLoc==SYSTEM)
			return (NULL);
	}
	return (NULL);
}
//---------------------------------------------------------
//Отчистка поверхности
//
bool ClearSurface(LPDIRECTDRAWSURFACE7 pSurface)
{
	DDSURFACEDESC2 ddSurfaceDesc;
	HRESULT hRet;

	ZeroMemory(&ddSurfaceDesc,sizeof(ddSurfaceDesc));
	ddSurfaceDesc.dwSize=sizeof(ddSurfaceDesc);

	hRet=pSurface->Lock(NULL,&ddSurfaceDesc,
			DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,NULL);
	if(FAILED(hRet))
	{
		return (false);
	}
	UINT surfaceWidth=ddSurfaceDesc.lPitch;
	UINT surfaceHeight=ddSurfaceDesc.dwHeight;

	char *buf=(char*)ddSurfaceDesc.lpSurface;
	ZeroMemory(buf,surfaceWidth*surfaceHeight);
	pSurface->Unlock(NULL);
	return (true);
}
//------------------------------------------------------
//Функция устанавливает прозрачные цвета для 16 битной поверхностей.
bool SetTransparentColors(LPDIRECTDRAWSURFACE7 pointer)
{
	//Установить цветовой ключ для заданного диапазона цветов
	DDCOLORKEY ddColorKey;
	ddColorKey.dwColorSpaceLowValue = TRANSCOLOR;
	ddColorKey.dwColorSpaceHighValue = TRANSCOLOR;
	HRESULT result = pointer->SetColorKey(DDCKEY_SRCBLT, &ddColorKey);
	if (result != DD_OK)return false;//если попытка не удалась, выйти с ошибкой
	return true;
}
//функция определяющая адрес поверхности
char* SurfacePointers(LPDIRECTDRAWSURFACE7 pSurface)
{
	char* addr;
	//Инициализировать структуру, описывающую поверхность
	DDSURFACEDESC2 ddSurfaceDesc;
	memset (&ddSurfaceDesc, 0, sizeof(DDSURFACEDESC2));
	ddSurfaceDesc.dwSize = sizeof (DDSURFACEDESC2);

	HRESULT result;
	BOOL exitLoop = FALSE;
	do
	{
		//Попытаемся заблокировать поверхность
		result = pSurface ->Lock(NULL, &ddSurfaceDesc,
			DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,
			NULL);
		
		//Если блокировка не состоялась из-за
		//потери поверхности, восстановить поверхность
		//и попытаться снова
		if (result == DDERR_SURFACELOST)
		{
			pSurface->Restore();
		}
		else
			exitLoop = TRUE;
	}
	while (!exitLoop);
	//Если блокировка поверхности прошла нормально...
	if (result == DD_OK)
	{
		//адресс поверхности
		addr = (char*)ddSurfaceDesc.lpSurface;
		//Разблокировка вторичной поверхности
		pSurface->Unlock(NULL);
	}
	return addr;
}
//------------------------------------------------------
//Вывод своего вторичного буфера на первичную поверхность
void Flip()
{
	if(!bActive)return;
	RECT rPic;
	//Установка размеров копируемого блока данных
	SetRect(&rPic, 0, 0, SizeWindowX, SizeWindowY);
	//печать на первичную поверхность
	pPrimarySurface->BltFast(0,0, buffer, &rPic,DDBLTFAST_WAIT);
}
//******************************************************************************
//---------------------------------------------------------
//Загрузка на 16 битную поверхность 8 битных и 24 битных растров
//пример - int background;
//		   load_bmp(&background,"data\\menu\\menu-main.bmp");
// pointer - указывает на номер в массиве поверхностей
LPDIRECTDRAWSURFACE7 load_bmp(char *filename)
{
	LPDIRECTDRAWSURFACE7 surface;
	//--------------------------------------------------------
	BYTE* pBmp;
	DWORD dwBmpSize;
	DWORD dwFileLength;
	DWORD nBytesRead;
	BITMAPINFO* pBmpInfo;
	BYTE*		pPixels;
	HDC hdc;
	//--------------------------------------------------------
	//Открытие файла с графическими данными
	HANDLE hFile=CreateFile(filename, GENERIC_READ,
			FILE_SHARE_READ, NULL,OPEN_EXISTING, 0, NULL);
	if (hFile==INVALID_HANDLE_VALUE)
		return (NULL);//если файла нет выйти с ошибкой
	//Получение размера файла и размера данных
	dwFileLength=GetFileSize (hFile, NULL) ;//размер файла
	dwBmpSize=dwFileLength-sizeof(BITMAPFILEHEADER);//размер битовой карты
	//Выделение памяти под битовую карту
	pBmp= (BYTE*) malloc(dwBmpSize);
	SetFilePointer(hFile, sizeof(BITMAPFILEHEADER), NULL, FILE_BEGIN);
	//Чтение файла в битовую карту
	ReadFile(hFile, (LPVOID)pBmp, dwBmpSize, &nBytesRead, NULL);
	//закрыть файл
	CloseHandle(hFile);
	//информация о растре
	pBmpInfo=(BITMAPINFO*)pBmp;
	//размеры растра
	int lx=((BITMAPINFOHEADER*)pBmp)->biWidth;
	int ly=((BITMAPINFOHEADER*)pBmp)->biHeight;
	//-----------------------------------------
	//если поверхность существует - уничтожить ее
	surface=CreateSurface(lx,ly);//создать поверхность
	if(surface==NULL)return(NULL);//если поверхности нет - выйти с ошибкой
	SetTransparentColors(surface);//задать поверхности прозрачный цвет
	//-----------------------------------------
	if(((BITMAPINFOHEADER*)pBmp)->biBitCount==8)//если растр 8 бит
	{	//задать новый адресс данных, так как файл 8 бит и надо учесть размер палитры
		pPixels=pBmp+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
	}
	//-----------------------------------------
	if(((BITMAPINFOHEADER*)pBmp)->biBitCount==16	//если растр 16 бит
		||((BITMAPINFOHEADER*)pBmp)->biBitCount==24 //если растр 24 бита
		||((BITMAPINFOHEADER*)pBmp)->biBitCount==32)//если растр 32 бита
	{
		pPixels=pBmp+sizeof(BITMAPINFOHEADER);
	}
	//-----------------------------------------
	//Получение заголовка контекста устройства внеэкранной поверхности
	if ((surface->GetDC(&hdc)) == DD_OK)
	{
		//Копирование графических данных из памяти на внеэкранную поверхность средствами GDI
		//очень удобно для загрузки
		StretchDIBits(hdc , 0, 0,  lx, ly, 0, 0, lx, ly, pPixels, pBmpInfo, 0, SRCCOPY);
		surface->ReleaseDC(hdc);
	}
	free(pBmp);
	return (surface);
	//-----------------------------------------
}// конец процедуры load_bmp
