ドクペリアンベアー モッカディートのスクリーンセーバー

横軸の移動をTimeベースにした。
scrnsaveの実装が気にくわないので、_tWinMain に換えた
ダブルバッファリング
100ms 以下のタイマ処理内で一か所でもStretchBlt使うと重いので排除(というかStretchBlt自体で105ms使用とかなにそれ.)

過去版:
http://d.hatena.ne.jp/ore_de_work/20130317#1363466551
http://d.hatena.ne.jp/ore_de_work/20130315#1363278698
http://d.hatena.ne.jp/ore_de_work/20130226#1362908014
http://d.hatena.ne.jp/ore_de_work/20130225#1361719138

/* ドクペリアンベアー.cpp */
#include "stdafx.h"

#include <windows.h>
#include <windowsx.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <tchar.h>
#include "resource.h"
#include <stdio.h>

#if 1
#    include <mmsystem.h> 
#    pragma comment (lib, "winmm.lib")
#    define OS_GET_TIME() timeGetTime()
#else
#    define OS_GET_TIME() GetTickCount()
#endif

#define MAX_LOADSTRING 255
#define ASSERT(x)  if(!(x)) __asm{ int 3 }
/* グローバル変数 */
HINSTANCE hMainInstance;
LONG run = 0;

TCHAR szTitle[MAX_LOADSTRING];
TCHAR szWindowClass[MAX_LOADSTRING];
TCHAR szInifile[MAX_LOADSTRING];

int mouseMoveCtr = 0;
int settingMode = 0;
int previewMode = 0;

LONG  m;
double x;

int Speed = 0;/* 速度 */
int Kuma = 0;/* 一列のくまさんの数 */
int Intr = 0;/* */

#define _countof(array) (sizeof(array)/sizeof(array[0]))

#define Save(x) WritePrivateProfileString(szTitle, x, szTemp, szInifile)
#define Load(x) GetPrivateProfileInt(szTitle, x, 0, szInifile)

/* ------ */
/* ダブルバッファ用 */
class buf_bmp
{
private:
	HBITMAP bmp, old;

public:
	LONG bmpw, bmph;
	HDC hdc;
	buf_bmp()
	{
		bmp = NULL;
		hdc = NULL;
		old = NULL;
	}
	void load(HDC dc, LONG w, LONG h )
	{
		if(old) SelectObject(hdc, old);
		if(hdc) DeleteDC(hdc);
		if(bmp) DeleteObject(bmp);

		bmp = CreateCompatibleBitmap(dc,w,h);

		bmpw = w;
		bmph = h;
		hdc = CreateCompatibleDC(dc);
		SetStretchBltMode(hdc, COLORONCOLOR);
		old = (HBITMAP)SelectObject(hdc, bmp);
	}
	~buf_bmp()
	{
		if(old) SelectObject(hdc, old);
		if(hdc) DeleteDC(hdc);
		if(bmp) DeleteObject(bmp);
	}

};
/* ------ */
class res_bmp
{
private:
	HBITMAP hbmp;
	HBITMAP oldbmp;
	HDC hdc_base;
public:
	HDC hdc_mem;
	LONG bmpw, bmph;

	res_bmp(HDC hdc, int id)
	{
		BITMAP bmp_info;
		hbmp = LoadBitmap(hMainInstance, MAKEINTRESOURCE(id));
		GetObject(hbmp, sizeof(BITMAP), &bmp_info);

		hdc_base = hdc;
		hdc_mem = CreateCompatibleDC(hdc_base);
		oldbmp = (HBITMAP)SelectObject(hdc_mem, hbmp );

		bmpw = bmp_info.bmWidth;
		bmph = bmp_info.bmHeight;

	}
	virtual ~res_bmp()
	{
		if(oldbmp) SelectObject(hdc_mem, oldbmp);
		if(hdc_mem) DeleteDC(hdc_mem); 
		if(hbmp) DeleteObject(hbmp);
	}

};

/* ------ */
class dc
{
private:
	HWND hwnd;
public:
	HDC hdc;
	dc(HWND h) { hwnd =h; hdc = GetDC(h);};
	virtual ~dc() {	ReleaseDC(hwnd,hdc); }
};

/* ------ */

/* アニメーション */
int anime[] =
{
	142, 143, 144, 145, 146, 147, 148, 149, 150, 151,
		152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 
		162, 0,
};
#define ANIME _countof(anime)

/* ------ */
buf_bmp  hibmp[ANIME];
buf_bmp  lobmp[ANIME];
buf_bmp  mainbmp;

/* ------ */
EXTERN_C BOOL WINAPI
_imp__ChangeWindowMessageFilter( UINT message, DWORD dwFlag )
{
	return FALSE;
}

/* ------ */
LONG dskw, dskh; // 画面全体
LONG scrw, scrh; // 実際のクライアント領域
LONG dstw, dsth; // 実際bmpのサイズ

/* ----- */
void Mokkadeet(HDC hdc, int id)
{
	SetStretchBltMode(hdc, COLORONCOLOR);
	res_bmp res(hdc, anime[id]);

	hibmp[id].load(hdc,dstw,dstw);
	lobmp[id].load(hdc,dstw,dstw);

	StretchBlt(hibmp[id].hdc, 0, 0, dstw, dsth, res.hdc_mem, 0, 0 , res.bmpw, res.bmph , SRCCOPY);
	StretchBlt(lobmp[id].hdc, +dstw, 0, -dstw, dsth, res.hdc_mem, 0, 0 , res.bmpw, res.bmph , SRCCOPY);

}
/* ------ */
void LoadMokkadeet(HWND hWnd)
{
	LONG i;
	RECT rc;
	dc d(hWnd);
	GetClientRect(hWnd, &rc);

	for( m = 0; m < ANIME; m++ )  if(anime[m] == 0) break;

	if(previewMode) {
		GetClientRect(GetDesktopWindow(), &rc);
		dskw = rc.right - rc.left;
		dskh = rc.bottom - rc.top;
	}

	scrw = rc.right - rc.left;
	scrh = rc.bottom - rc.top;

	/* スクリーンサイズからモッカディートのサイズを算出 */
	res_bmp res(d.hdc, anime[0]);
	dstw = scrw / Kuma;
	dsth = res.bmpw ? dstw * res.bmph / res.bmpw : dstw;

	/* bitmap 読込 (上) 方向 ← ← ← ← ←*/
	for( i = 0; i < m; i++ )
		Mokkadeet(d.hdc, i);

	mainbmp.load(d.hdc, scrw, scrh);
	SetStretchBltMode(mainbmp.hdc, COLORONCOLOR);

}

/* ----- */
void OnTimer(HWND hWnd)
{
	BOOL bRedrow = FALSE;
	LONG y, cx1, cx2;
	RECT rc;

	dc d(hWnd);
	GetClientRect(hWnd, &rc);

	scrw = rc.right - rc.left;
	scrh = rc.bottom - rc.top;

	/* スクリーンサイズからモッカディートのサイズを算出 */
	dstw = scrw / Kuma;
	dsth = hibmp[run].bmpw ? dstw * hibmp[run].bmph / hibmp[run].bmpw : 0;

	/* スクリーンサイズ変わったら再計算 StretchBlt重い. */
	if( hibmp[run].bmpw == 0 ||
		hibmp[run].bmpw != dstw || hibmp[run].bmph != dsth ||
		lobmp[run].bmpw != dstw || lobmp[run].bmph != dsth)
	{
		Mokkadeet(d.hdc, run);
		StretchBlt(mainbmp.hdc,0, 0, scrw, scrh,hibmp[run].hdc, 0, 0, 1, 1, SRCCOPY);
		bRedrow = TRUE;
	}

	/* 速度 と 位置補正 */
	y = ((scrh - (dsth * 2)) / 10);

	cx1 = (LONG)  - ((previewMode) ? x * scrw / dskw : x);
	cx1 %= dstw;
	//cx1 -= dstw; もともと負数

	cx2 = (LONG)  + ((previewMode) ? x * scrw / dskw : x);
	cx2 %= dstw;
	cx2 -= dstw; // スクリーン外にする

	/* (上) 方向 ← ← ← ← ← */
	for(; cx1 < rc.right - rc.left; cx1 += dstw)
		BitBlt(mainbmp.hdc, cx1, y, dstw, dsth,
			hibmp[run].hdc, 0, 0, SRCCOPY);

	/* (下) 方向 → → → → → */
	for(; cx2 < rc.right + dstw; cx2 += dstw)
		BitBlt(mainbmp.hdc, cx2, rc.bottom - rc.top - dsth - y, dstw, dsth,
			lobmp[run].hdc,0,0,SRCCOPY);

	if(bRedrow)
	{
		// 画面全体
		BitBlt(d.hdc, 0,0, scrw, scrh, mainbmp.hdc,0,0,SRCCOPY);
	}
	else
	{
		// 上と下のみ
		BitBlt(d.hdc, 0,y, scrw, dsth, mainbmp.hdc,0,y,SRCCOPY);
		BitBlt(d.hdc, 0,rc.bottom - rc.top - dsth - y, scrw, dsth, mainbmp.hdc,0,rc.bottom - rc.top - dsth - y,SRCCOPY);
	}

	x = (double)OS_GET_TIME() / 1000.0 *  Speed;

}
/* ------ */
void LoadSetting()
{
	RECT rc;
	GetWindowRect(GetDesktopWindow(), &rc);
	Intr = Load(_T("INTR"));
	Kuma = Load(_T("KUMA"));
	Speed = Load(_T("SPEED"));

	if(Intr==0) Intr = 24;
	if(Kuma==0) Kuma = 5;
	if(Speed==0) Speed = 5 * 41; // 秒速5センチメートル
}
/* ------ */
void OnCommandChange(HWND hWnd, UINT ID)
{
	TCHAR szTemp[MAX_LOADSTRING];

	switch (ID)
	{
	case IDC_KUMA:
		Kuma = GetDlgItemInt(hWnd, IDC_KUMA, NULL, FALSE);
		if(Kuma==0) Kuma = 5;
		_stprintf(szTemp, _T("%d"), Kuma);
		Save(_T("KUMA"));
		break;
	case IDC_INTR:
		Intr = GetDlgItemInt(hWnd, IDC_INTR, NULL, FALSE);
		KillTimer(hWnd,1);
		if(Intr==0) Intr = 24;
		SetTimer(hWnd,1,1000/Intr, NULL);
		_stprintf(szTemp, _T("%d"), Intr);
		Save(_T("INTR"));
		break;
	case IDC_SPEED:
		if(Intr==0) Intr = 24;
		Speed = GetDlgItemInt(hWnd, IDC_SPEED, NULL, FALSE);
		if(Speed==0) Speed = 5 * 41; // 秒速5センチメートル
		_stprintf(szTemp, _T("%d"), Speed);
		Save(_T("SPEED"));
		break;
	}

	switch (ID)
	{
	case IDC_KUMA:
	case IDC_INTR:
	case IDC_SPEED:
		{
			_stprintf(szTemp, _T("秒速 %.2f センチメートル"), (double)Speed / 41.0);
			SetDlgItemText(hWnd, IDC_SPEED_NOTE, szTemp);
		}
		break;
	}
}

/* ------ */
void StoreSettingToEdit(HWND hWnd)
{
	SetDlgItemInt(hWnd, IDC_SPEED, Speed, FALSE);
	SetDlgItemInt(hWnd, IDC_INTR, Intr, FALSE);
	SetDlgItemInt(hWnd, IDC_KUMA, Kuma, FALSE);
}

/* ------ */
/* スクリーンセーバー処理 */
LRESULT CALLBACK ScreenSaverProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;

	switch(uMsg)
	{
	case WM_CREATE:
		LoadSetting();
		LoadMokkadeet(hWnd);

		SetTimer(hWnd,2,10000, NULL);
		SetTimer(hWnd,1,1000/Intr, NULL);

		if(settingMode == 0 && previewMode == 0) 
		{
			SetCapture(hWnd);
			ShowCursor(FALSE);
		}
		break;

	case WM_TIMER:
		{
			run++;
			if(run >= m) run = 0;

			if( wParam == 1)
				OnTimer(hWnd);

			if( wParam == 2)
				SetWindowPos(hWnd, HWND_TOPMOST, 0,0,0,0,  SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE );
		}
		break;

	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		BitBlt(hdc, 0,0, scrw, scrh, mainbmp.hdc,0,0,SRCCOPY);
		EndPaint(hWnd, &ps);
		break;

	case WM_DESTROY:
		if(settingMode == 0 && previewMode == 0)
			ReleaseCapture();
		KillTimer(hWnd,1);
		ShowCursor(TRUE);//マウスカーソルを出す
		PostQuitMessage(0);
		break;

	case WM_MOUSEMOVE:
		//マウス移動イベント12回以上で終了
		if(previewMode == 1) break;
		if(settingMode == 1) break;
		if(++mouseMoveCtr<12) break;

	case	 WM_LBUTTONDOWN://マウス左ボタン押下
	case	 WM_RBUTTONDOWN://マウス右ボタン押下
	case	 WM_KEYDOWN://キーボード押下
	case	 WM_SYSKEYDOWN://システムキーの押下
		if(previewMode == 1) break;
		if(settingMode == 1) break;
		PostMessage(hWnd, WM_CLOSE, 0, 0);
		break;

	case WM_SYSCOMMAND:
		if((wParam & 0xFFF0)==SC_SCREENSAVE)
			PostMessage(hWnd, WM_CLOSE, 0, 0);
		if((wParam & 0xFFF0)==SC_MONITORPOWER)
			PostMessage(hWnd, WM_CLOSE, 0, 0);
		break;

	default:
		return DefWindowProc(hWnd,uMsg,wParam,lParam);
	}
	return 0;
}

/* ------ */
/* 設定ダイアログ  */
INT_PTR CALLBACK ScreenSaverConfigureDialog(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	TCHAR szTemp[MAX_LOADSTRING];
	HICON hIcon;
	switch(uMsg) 
	{
	case WM_INITDIALOG: 
		uMsg = WM_CREATE;

		hIcon = LoadIcon(hMainInstance, MAKEINTRESOURCE(IDR_MAINFRAME));
		SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
		SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);

	case WM_DESTROY:
		ScreenSaverProc( hWnd,  uMsg,  wParam,  lParam);
	case WM_CREATE:
		StoreSettingToEdit(hWnd);
		break;
	case WM_COMMAND:			
		if(HIWORD(wParam) == EN_CHANGE) OnCommandChange(hWnd, LOWORD(wParam) );
		break;
	case WM_TIMER:
		{
		static DWORD b = 0;
#define avg(base, x) ((base) ? ( ( (base) + (double)(x)) / 2.0) : (double)(x))
		static double es = 0;
		static double a = 0;
		DWORD s, e;
		s = OS_GET_TIME();
		ScreenSaverProc( GetDlgItem(hWnd, ID_SCR),  WM_TIMER,  1,  lParam);
		e = OS_GET_TIME();
		_stprintf(szTemp, _T(
			"w=%3d, h=%3d m/a=%2d/%2d run=%2d\r\n"
			"sb=%d [%3.3f] es=%d [%3.3f]\r\n"
			"x=%5.2f intr=%5.2f\r\n"),
			dstw, dsth, m, ANIME, run,
			s - b, a, e -s, es,
			x, 1000.0/Intr);
		b ? a = avg( a ,s - b ) : (void)0;
		b = s;
		es = avg(es, e - s);
		SetDlgItemText(hWnd, IDC_DEBUG, szTemp);
	   }
		break;
	case WM_CLOSE:
		EndDialog(hWnd, 0);
		break;
	default:
		break;
	}
	return FALSE;
}
/* ------ */
ATOM MyRegisterClass(HINSTANCE hInstance, WNDPROC lpfnProc)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= lpfnProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)GetStockObject(BLACK_BRUSH);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= LoadIcon(hInstance, MAKEINTRESOURCE(IDR_MAINFRAME));

	return RegisterClassEx(&wcex);
}
/* ------ */
int APIENTRY _tWinMain(HINSTANCE hInstance,
					   HINSTANCE hPrevInstance,
					   LPTSTR    lpCmdLine,
					   int       nCmdShow)
{
	HWND hWndParent = NULL;
	RECT rc;
	MSG msg;
	hMainInstance = hInstance; // グローバル変数にインスタンス処理を格納します。

	settingMode = 1;
	previewMode = 1;

	// グローバル文字列を初期化しています。
	LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
	LoadString(hInstance, IDS_APP_TITLE, szWindowClass, MAX_LOADSTRING);
	_stprintf( szInifile, "%s.ini", szTitle );

	if(__argc >= 2 && (__argv[1][0] == '-' || __argv[1][0] == '/'))
	{
		switch(__argv[1][1])
		{
		case 'P': case 'p': // プレビュー表示
			previewMode = 1;
			settingMode = 0;
			// 2番目の引数をウィンドウハンドルに変換
			if(__argc == 3)
				hWndParent = (HWND)(LONG_PTR) (_tstol(__argv[2]));
			break;
		case 'S': case 's': // スクリーンセーバー実行
			previewMode = 0;
			settingMode = 0;
			// 2番目の引数をウィンドウハンドルに変換
			if(__argc == 3)
				hWndParent = (HWND)(LONG_PTR) (_tstol(__argv[2]));
			break;
		case 'A': case 'a':  // パスワード設定
			// 略
			return 0;
			break;
		case 'C': case 'c':  // 設定画面表示
			previewMode = 1;
			settingMode = 1;
			break;
		}
	}
	if(hWndParent == NULL)
		hWndParent = GetDesktopWindow();
	GetClientRect(hWndParent, &rc);

	if(settingMode)
	{
		// 設定画面の表示
		settingMode = 1;
		return (int)DialogBox(hInstance, reinterpret_cast<LPCTSTR>(DLG_SCRNSAVECONFIGURE),
			hWndParent, ScreenSaverConfigureDialog);
	}
	else
	{
		HWND hMainWnd;
		MyRegisterClass(hInstance, ScreenSaverProc);
		hMainWnd = CreateWindow(szWindowClass, szTitle,
			WS_CHILD | WS_VISIBLE,
			rc.left, rc.top, rc.right, rc.bottom, hWndParent, NULL, hInstance, NULL);

		if (!hMainWnd) return FALSE;


		SetWindowPos(hMainWnd, HWND_TOPMOST, rc.left, rc.top, rc.right, rc.bottom, SWP_SHOWWINDOW );

		UpdateWindow(hMainWnd);

		while (GetMessage(&msg, NULL, 0, 0) >  0) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		return (int) msg.wParam;
	}
}