﻿//
//	Draw Bitmap sample
//

#include "sample2.h"
#include "resource.h"

#include <olectl.h>		//	for OLE_HANDLE
#include <ole2.h>		//	for CreateStreamOnHGlobal()

//	オフスクリーンサーフェスの生成
BOOL CreateOffscreenSurface(LPDIRECTDRAWSURFACE7 *lplpSurface, int ww, int hh)
{
	HRESULT hr;

	//	サーフェスの生成
	DDSURFACEDESC2	ddsd;
	ZeroMemory(&ddsd, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;		//	ビデオメモリ上
//	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;	//	システムメモリ上
	ddsd.dwWidth = ww;
	ddsd.dwHeight = hh;

	hr = lpDD->CreateSurface(&ddsd, lplpSurface, NULL);
	if(hr != DD_OK){
		*lplpSurface = NULL;
		//	オフスクリーンサーフェスの生成に失敗した
		return FALSE;
	}

	return TRUE;
}

//	ビットマップリソースからサーフェスを生成
BOOL CreateOffscreenSurfaceFromResource(LPDIRECTDRAWSURFACE7 *lplpSurface, WORD wResourceID, LPSIZE psi)
{
	HDC				hSrc, hDest;
	HBITMAP			hBitmap, hBitmapPrev;

	//	ビットマップリソースのハンドルを得ます
	if((hBitmap = (HBITMAP)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(wResourceID),
		IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION)) == NULL){
		//	ビットマップリソースが見つからなかった
		return FALSE;
	}

	//	ビットマップの情報を得ます
	BITMAP bmp;
	if(!GetObject(hBitmap, sizeof(BITMAP), &bmp)){
		//	ビットマップ情報の取得に失敗した
		return FALSE;
	}

	//	ビットマップリソースと同じ大きさのサーフェスを生成します
	if(!CreateOffscreenSurface(lplpSurface, bmp.bmWidth, bmp.bmHeight)){
		//	ビットマップサーフェスの生成に失敗した
		return FALSE;
	}

	//	ビットマップリソースをサーフェスに転送します
	hSrc = CreateCompatibleDC(NULL);
	hBitmapPrev = (HBITMAP)SelectObject(hSrc, hBitmap);
	(*lplpSurface)->GetDC(&hDest);
	BitBlt(hDest, 0, 0, bmp.bmWidth, bmp.bmHeight, hSrc, 0, 0, SRCCOPY);
	(*lplpSurface)->ReleaseDC(hDest);
	DeleteObject(SelectObject(hSrc, hBitmapPrev));
	DeleteDC(hSrc);
	DeleteObject(hBitmap);

	//	サイズを保持
	if(psi != NULL){
		psi->cx = bmp.bmWidth;
		psi->cy = bmp.bmHeight;
	}

	return TRUE;
}

//	画像ファイルからサーフェスを生成
BOOL CreateOffscreenSurfaceFromBmpFile(LPDIRECTDRAWSURFACE7 *lplpSurface, LPCSTR lpFilename, LPSIZE psi)
{
	HDC			hSrc, hDest;
	HBITMAP		hBitmap, hBitmapPrev;

	//	ビットマップファイルのハンドルを得ます
	if((hBitmap = (HBITMAP)LoadImage(GetModuleHandle(NULL), lpFilename,
		IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION)) == NULL){
		return FALSE;
	}

	//	ビットマップの情報を得ます
	BITMAP bmp;
	if(!GetObject(hBitmap, sizeof(BITMAP), &bmp)){
		//	ビットマップ情報の取得に失敗した
		return FALSE;
	}

	//	ビットマップリソースと同じ大きさのサーフェスを生成します
	if(!CreateOffscreenSurface(lplpSurface, bmp.bmWidth, bmp.bmHeight)){
		//	ビットマップサーフェスの生成に失敗した
		return FALSE;
	}

	//	ビットマップリソースをサーフェスに転送します
	hSrc = CreateCompatibleDC(NULL);
	hBitmapPrev = (HBITMAP)SelectObject(hSrc, hBitmap);
	(*lplpSurface)->GetDC(&hDest);
	BitBlt(hDest, 0, 0, bmp.bmWidth, bmp.bmHeight, hSrc, 0, 0, SRCCOPY);
	(*lplpSurface)->ReleaseDC(hDest);
	DeleteObject(SelectObject(hSrc, hBitmapPrev));
	DeleteDC(hSrc);
	DeleteObject(hBitmap);

	//	サイズを保持
	if(psi != NULL){
		psi->cx = bmp.bmWidth;
		psi->cy = bmp.bmHeight;
	}

	return TRUE;
}

BOOL CreateOffscreenSurfaceFromJpgFile(LPDIRECTDRAWSURFACE7 *lplpSurface, LPCSTR lpFilename, LPSIZE psi)
{
	OLE_HANDLE	hOle = NULL;
	IPicture*	pPicture;		//	ピクチャインターフェース

	HDC			hSrc, hDest;
	HBITMAP		hBitmap, hBitmapPrev;

	//	画像ファイルを開く
	HANDLE hFile = CreateFile(lpFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
	if(hFile == INVALID_HANDLE_VALUE){
		return NULL;	//	ファイルが開けなかった
	}
	DWORD dwFileSize = GetFileSize(hFile, NULL);
	HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, dwFileSize);
	LPVOID pvData = GlobalLock(hGlobal);
	DWORD dwRead = 0;
	ReadFile(hFile, pvData, dwFileSize, &dwRead, NULL);
	GlobalUnlock(hGlobal);
	CloseHandle(hFile);

	//	画像を読み込む
	IStream* pStream = NULL;
	CreateStreamOnHGlobal(hGlobal, TRUE, &pStream);
	OleLoadPicture(pStream, dwFileSize, FALSE, IID_IPicture, (LPVOID *)&pPicture);
	pStream->Release();

	//	ビットマップハンドルを取得
	short type;
	pPicture->get_Type(&type);
	if(type == PICTYPE_BITMAP){
		pPicture->get_Handle(&hOle);
	}

	hBitmap = (HBITMAP)hOle;

	//	ビットマップの情報を得ます
	BITMAP bmp;
	if(!GetObject(hBitmap, sizeof(BITMAP), &bmp)){
		//	ビットマップ情報の取得に失敗した
		return FALSE;
	}

	//	ビットマップリソースと同じ大きさのサーフェスを生成します
	if(!CreateOffscreenSurface(lplpSurface, bmp.bmWidth, bmp.bmHeight)){
		//	ビットマップサーフェスの生成に失敗した
		return FALSE;
	}

	//	ビットマップリソースをサーフェスに転送します
	hSrc = CreateCompatibleDC(NULL);
	hBitmapPrev = (HBITMAP)SelectObject(hSrc, hBitmap);
	(*lplpSurface)->GetDC(&hDest);
	BitBlt(hDest, 0, 0, bmp.bmWidth, bmp.bmHeight, hSrc, 0, 0, SRCCOPY);
	(*lplpSurface)->ReleaseDC(hDest);
	DeleteObject(SelectObject(hSrc, hBitmapPrev));
	DeleteDC(hSrc);
	DeleteObject(hBitmap);

	//	サイズを保持
	if(psi != NULL){
		psi->cx = bmp.bmWidth;
		psi->cy = bmp.bmHeight;
	}

	pPicture->Release();	//	ピクチャインターフェースを解放

	return TRUE;
}

//	画面更新
void UpdateFrame(void)
{
	static int x1 = 100, x2 = 200, x3 = 300;

	//	画面消去
	DDBLTFX		ddBltFx;
	ZeroMemory(&ddBltFx, sizeof(DDBLTFX));
	ddBltFx.dwSize = sizeof(DDBLTFX);
	lpBackSurface->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddBltFx);

	//	ビットマップリソースを描画
	RECT rc;
	SetRect(&rc, 0, 0, siRes.cx, siRes.cy);
	lpBackSurface->BltFast(x1, 100, lpSurfaceRes, &rc, DDBLTFAST_WAIT);

	//	ビットマップファイルを描画
	SetRect(&rc, 0, 0, siBmp.cx, siBmp.cy);
	lpBackSurface->BltFast(x2, 200, lpSurfaceBmp, &rc, DDBLTFAST_WAIT);

	//	JPEGファイルを描画
	SetRect(&rc, 0, 0, siJpg.cx, siJpg.cy);
	lpBackSurface->BltFast(x3, 300, lpSurfaceJpg, &rc, DDBLTFAST_WAIT);

	//	移動
	x1--; if(x1 < 0){ x1 = SCREEN_WIDTH - siRes.cx; }
	x2--; if(x2 < 0){ x2 = SCREEN_WIDTH - siBmp.cx; }
	x3--; if(x3 < 0){ x3 = SCREEN_WIDTH - siJpg.cx; }

	//	フリップ
	lpPrimarySurface->Flip(NULL, DDFLIP_WAIT);

	return;
}

//	DirectDrawの初期化
BOOL DirectDrawInit(HWND hWnd)
{
	HRESULT hr;

	//	DirectDraw7オブジェクトの作成
	hr = DirectDrawCreateEx(NULL, (void**)&lpDD, IID_IDirectDraw7, NULL);
	if(hr != DD_OK){
		DebugMessage("DirectDraw7オブジェクトの生成に失敗しました\n");
		return FALSE;
	}

	//	画面モードを設定する
	hr = lpDD->SetCooperativeLevel(hWnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
	if(hr != DD_OK){
		DebugMessage("画面モードの設定に失敗しました\n");
		RELEASE(lpDD);
		return FALSE;
	}

	//	画面サイズと色深度を設定する
	hr = lpDD->SetDisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, 0, 0);
	if(hr != DD_OK){
		DebugMessage("画面サイズの設定に失敗しました\n");
		RELEASE(lpDD);
		return FALSE;
	}

	//	サーフェースを作成する
	DDSURFACEDESC2 ddsd;
	ZeroMemory(&ddsd, sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
	ddsd.dwBackBufferCount = 1;

	//	プライマリサーフェース作成
	hr = lpDD->CreateSurface(&ddsd, &lpPrimarySurface, NULL);
	if(hr != DD_OK){
		DebugMessage("プライマリーサーフェスの生成に失敗しました\n");
		RELEASE(lpDD);
		return FALSE;
	}

	//	バックバッファ作成
	DDSCAPS2 ddscaps;
	ZeroMemory(&ddscaps, sizeof(ddscaps));
	ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
	hr = lpPrimarySurface->GetAttachedSurface(&ddscaps, &lpBackSurface);
	if(hr != DD_OK){
		DebugMessage("バックサーフェスの生成に失敗しました\n");
		RELEASE(lpPrimarySurface);
		RELEASE(lpDD);
		return FALSE;
	}

	return TRUE;
}

//	DirectDrawの破棄
BOOL DirectDrawExit(void)
{
	RELEASE(lpSurfaceJpg);
	RELEASE(lpSurfaceBmp);
	RELEASE(lpSurfaceRes);

	RELEASE(lpBackSurface);
	RELEASE(lpPrimarySurface);
	RELEASE(lpDD);

	return TRUE;
}

//	Windowsメッセージの処理
long CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg){
	case WM_KEYDOWN:
		switch(wParam){
		case VK_ESCAPE:
			PostMessage(hWnd, WM_CLOSE, 0, 0);
			break;
		}
		break;
	case WM_SETCURSOR:
		SetCursor(NULL);	//	これがないとカーソルが表示される
		return TRUE;
	case WM_DESTROY:
		DirectDrawExit();
		PostQuitMessage(0);
		break;
	default:
		break;
	}

	return DefWindowProc(hWnd, msg, wParam, lParam);
}

//	プログラムの初期化(ウィンドウ作成など)
BOOL Init(HINSTANCE hInstance)
{
	hInst = hInstance;

	//	ウィンドウクラスの登録
	WNDCLASS	wc;
	wc.style = CS_HREDRAW | CS_VREDRAW;// | CS_DBLCLKS;
	wc.lpfnWndProc = (WNDPROC)WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = sizeof(DWORD);
	wc.hInstance = hInst;
	wc.hIcon = NULL;
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = APP_CLASS;

	if(!RegisterClass(&wc)){
		DebugMessage("ウィンドウクラスの登録に失敗しました\n");
		return FALSE;
	}

	//	ウィンドウの生成
	hWndMain = CreateWindowEx(WS_EX_TOPMOST,
		APP_CLASS, APP_TITLE,
		WS_POPUP | WS_VISIBLE,
		0,
		0,
		GetSystemMetrics(SM_CXSCREEN),
		GetSystemMetrics(SM_CYSCREEN),
		NULL, NULL, hInst, NULL);
	if(!hWndMain){
		DebugMessage("ウィンドウの生成に失敗しました\n");
		return FALSE;
	}

	//	DirectDrawの初期化
	if(!DirectDrawInit(hWndMain)){
		DebugMessage("DirectDrawの初期化に失敗しました\n");
		return FALSE;
	}

	//	画像の読み込みとサーフェスの構築
	CreateOffscreenSurfaceFromResource(&lpSurfaceRes, IDB_ANIMAL1, &siRes);
	CreateOffscreenSurfaceFromBmpFile(&lpSurfaceBmp, "animal2.bmp", &siBmp);
	CreateOffscreenSurfaceFromJpgFile(&lpSurfaceJpg, "animal3.jpg", &siJpg);

	//	準備ができたら描画
	ShowWindow(hWndMain, SW_SHOW);
	UpdateWindow(hWndMain);

	return TRUE;
}

//	Windowsメッセージの受信
int MainThread(void)
{
	MSG		msg;

	while(1){
		if(PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)){
			if(!GetMessage(&msg, NULL, 0, 0)){
				break;
			}
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		else{
			//	メッセージが来ない時はひたすら描画処理を呼ぶ
			UpdateFrame();
		}
	}

	return msg.wParam;
}

//	デバッグ文出力
void DebugMessage(const char *pFormat, ...)
{
	char	szBuff[1024];
	va_list	argptr;

	va_start(argptr, pFormat);
	vsprintf(szBuff, pFormat, argptr);

	va_end(argptr);

	OutputDebugString(szBuff);

	return;
}

//	WinMain
int PASCAL WinMain(HINSTANCE hInst, HINSTANCE hPreInst, LPSTR lpCmdLine, int nCmdShow)
{
	Init(hInst);	//	プログラムの初期化(ウィンドウ作成など)
	MainThread();	//	メインスレッド

	return 0;
}
