ドクペリアンベアー モッカディートのスクリーンセーバー
横軸の移動を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; } }