WinAPIを使用して、ビットマップ画像を8枚読み込み、それを連続高速表示するプログラムを作成しています。
今はSetTimerを使ってWM_TIMERを受け取ったときに画像をInvalidateRect(再描画)しています。
以下のソースで動作はするのですが、WM_TIMERは整数ミリ秒でしか設定できず、精度も悪く優先順位も遅いようなので他の方法を考えています。
画像8枚を6.25msecで切り替えて表示するというのをESCAPEするまで繰り返したいのですが...。
リフレッシュレートは160Hzにあげています。
QueryPerformanceFrequencyというものを使えばいいのかなと思っていますが、どこでどう使えばいいのか、それをどう受け取って再描画すればいいのかわかりません。
どなたかご教授お願いします。ソースファイルを書いていただけたら嬉しいです。
#include<windows.h>
#define BMP_SUM8//画像の総数
#define TIMER_ID (100) // 作成するタイマの識別ID
#define TIMER_ELAPSE (6) // WM_TIMERの発生間隔
LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam){
HDC hdc;
PAINTSTRUCT ps;
HBITMAP hBitmap;
int i;
const char *filename[BMP_SUM]={"gazou0.bmp", "gazou1.bmp", "gazou2.bmp", "gazou3.bmp", "gazou4.bmp", "gazou5.bmp", "gazou6.bmp", "gazou7.bmp"};
static HDC hMemDC[BMP_SUM];
static BITMAP bmp;
static int bmp_index;//現在の画像番号
LONG lResult;
switch(uMsg) {
case WM_CREATE:
hdc=GetDC(hWnd);
for(i=0;i<BMP_SUM;i++){
hBitmap=(HBITMAP)LoadImage(0,filename[i],IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
hMemDC[i]=CreateCompatibleDC(hdc);
SelectObject(hMemDC[i],hBitmap);
}
GetObject(hBitmap,sizeof(BITMAP),&bmp);
DeleteObject(hBitmap);
ReleaseDC(hWnd,hdc);
return 0;
case WM_TIMER:
if( wParam != TIMER_ID )
{
break; // 識別IDが一致しないタイマメッセージはDefWindowProc()に任せる
}
if(++bmp_index >= BMP_SUM) bmp_index=0;
for(i = 0; i < BMP_SUM; i++){
InvalidateRect( hWnd, NULL, FALSE );
}
return 0;
case WM_DESTROY:
for(i=0;i<BMP_SUM;i++) DeleteDC(hMemDC[i]);
PostQuitMessage(0);
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
BitBlt(hdc,0,0,bmp.bmWidth,bmp.bmHeight,hMemDC[bmp_index],0,0,SRCCOPY);
EndPaint(hWnd,&ps);
return 0;
case WM_KEYDOWN:
switch((CHAR)wParam)
{
case VK_ESCAPE:
for(i=0;i<BMP_SUM;i++) DeleteDC(hMemDC[i]);
PostQuitMessage(0); //WM_QUITメッセージを出す
return 0;
}
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
入りきらないので2つに分けます。
続きは「WinAPIでの画像高速切り替え表示プログラム2」を見てください。
A 回答 (7件)
- 最新から表示
- 回答順に表示
No.7
- 回答日時:
// No.4:
int main(int argc, char** argv)
{
ULONG_PTR token = 0;
Gdiplus::GdiplusStartupInput input;
Gdiplus::GdiplusStartup(&token, &input, 0);
glutInit(&argc, argv);
glutInitWindowSize(g_winSize.cx, g_winSize.cy);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
glutCreateWindow("Precise Flip Test");
glutDisplayFunc(OnDisplay);
glutIdleFunc(OnIdle);
glutReshapeFunc(OnReshape);
glutKeyboardFunc(OnKeyboard);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.0, 0.5, 0.5, 1.0);
wchar_t fileName[1024] = {};
for (int i = 0; i < IMG_NUM; i++)
{
swprintf_s(fileName, L"image%02d.png", i); // 24/32bit PNG/BMP, 24bit TIFF/JPEG に対応。サイズは縦横 2^n のみ。
if (!g_texHolders[i].LoadFromFile(fileName))
{
return -1;
}
}
const BOOL isAvailableQPC = ::QueryPerformanceCounter(&g_start);
assert(isAvailableQPC);
glutMainLoop();
// 以下の解放処理は一応記述していますが、終了時に呼ばれません。
// GLUT では解放処理を OS に委ねています。
// WGL などを使ってすべて自前で制御するときは、明示的に解放/再作成できます。
for (int i = 0; i < IMG_NUM; i++)
{
g_texHolders[i].Release();
}
Gdiplus::GdiplusShutdown(token);
return 0;
}
以上です。
Direct3Dのサンプルは書きませんのであしからず。
No.6
- 回答日時:
// No.3:
void OnDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
{
glLoadIdentity();
glColor3d(1, 1, 0);
glDisable(GL_BLEND);
glRasterPos2d(-0.9, 0.9);
static char message[256];
sprintf_s(message, "%3u FPS", g_fpsValue);
DrawString(GLUT_BITMAP_HELVETICA_18, message);
}
glPopMatrix();
glRotated(2, 0, 0, 0.25);
glBindTexture(GL_TEXTURE_2D, g_texHolders[g_currentBmpIndex].GetTextureID());
glColor3d(1, 1, 1);
glEnable(GL_BLEND);
glBegin(GL_POLYGON);
{
// UV と頂点位置の設定。
const double wInv = static_cast<double>(g_texHolders[g_currentBmpIndex].GetWidth()) / g_winSize.cx;
const double hInv = static_cast<double>(g_texHolders[g_currentBmpIndex].GetHeight()) / g_winSize.cy;
glTexCoord2d(0, 0); glVertex2d(-wInv, +hInv);
glTexCoord2d(0, 1); glVertex2d(-wInv, -hInv);
glTexCoord2d(1, 1); glVertex2d(+wInv, -hInv);
glTexCoord2d(1, 0); glVertex2d(+wInv, +hInv);
}
glEnd();
glutSwapBuffers();
}
void OnIdle()
{
LARGE_INTEGER stop = {}, freq = {};
::QueryPerformanceCounter(&stop);
::QueryPerformanceFrequency(&freq);
const LONGLONG diffStartStop = stop.QuadPart - g_start.QuadPart;
if (diffStartStop * FPS_LIMIT < freq.QuadPart)
{
return;
}
static UINT fpsUpdateCounter;
const UINT FPS_UPDATE_SKIP_FRAME_COUNT = 160;
if (++fpsUpdateCounter >= FPS_UPDATE_SKIP_FRAME_COUNT)
{
g_fpsValue = (diffStartStop > 0) ?
static_cast<UINT>(freq.QuadPart / diffStartStop) : 9999;
fpsUpdateCounter = 0;
}
::QueryPerformanceCounter(&g_start);
g_currentBmpIndex = (g_currentBmpIndex + 1) % IMG_NUM;
glutPostRedisplay(); // GLUT では既定で垂直同期されるので、FPS は基本的にモニタのリフレッシュレート以上にはならない。
}
void OnReshape(int width, int height)
{
g_winSize.cx = width;
g_winSize.cy = height;
}
void OnKeyboard(unsigned char key, int x, int y)
{
if (key == 27) // Esc
{
exit(0);
}
}
No.5
- 回答日時:
// No.2:
class TexHolder
{
GLuint m_textureId;
UINT m_width;
UINT m_height;
public:
TexHolder()
: m_textureId(GL_INVALID_VALUE)
, m_width()
, m_height()
{
}
GLuint GetTextureID() const { return m_textureId; }
UINT GetWidth() const { return m_width; }
UINT GetHeight() const { return m_height; }
bool IsValid() const { return (m_textureId != GL_INVALID_VALUE && m_textureId != GL_INVALID_OPERATION); }
bool Generate()
{
this->Release();
glGenTextures(1, &m_textureId);
return this->IsValid();
}
void Release()
{
if (this->IsValid())
{
glDeleteTextures(1, &m_textureId);
m_textureId = GL_INVALID_VALUE;
m_width = 0;
m_height = 0;
}
}
bool LoadFromFile(LPCWSTR pFilePath)
{
if (!this->Generate())
{
this->Release();
return false;
}
Bitmap bmp(pFilePath);
if (bmp.GetLastStatus() != Ok)
{
this->Release();
return false;
}
m_width = bmp.GetWidth();
m_height = bmp.GetHeight();
const PixelFormat pf = bmp.GetPixelFormat();
if (m_width != m_height || !IsModulo2(m_width))
{
this->Release();
return false;
}
if (pf != PixelFormat32bppARGB && pf != PixelFormat24bppRGB)
{
this->Release();
return false;
}
BitmapData bmpData;
bmp.LockBits(&Rect(0, 0, m_width, m_height), ImageLockModeRead, pf, &bmpData);
glBindTexture(GL_TEXTURE_2D, m_textureId);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (pf == PixelFormat32bppARGB)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bmpData.Width, bmpData.Height, 0, GL_BGRA_EXT, GL_UNSIGNED_BYTE, bmpData.Scan0);
}
else
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bmpData.Width, bmpData.Height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, bmpData.Scan0);
}
glBindTexture(GL_TEXTURE_2D, 0);
bmp.UnlockBits(&bmpData);
return true;
}
virtual ~TexHolder()
{
this->Release();
}
};
namespace
{
const int IMG_NUM = 8;
const UINT FPS_LIMIT = 160;
TexHolder g_texHolders[IMG_NUM];
LARGE_INTEGER g_start;
UINT g_fpsValue;
UINT g_currentBmpIndex;
SIZE g_winSize = {800, 800};
}
No.4
- 回答日時:
OpenGLの話は長くなるので、GLUTとGDI+による簡単なソースのみにとどめて、詳しい解説は割愛します。
入門の鉄板サイトを紹介しておきますので、あとはご自分で調べてください。
OpenGLの勉強をこれから始める人、あるいはOpenGL用ウィンドウの生成などの面倒な部分を省きたい場合はGLUTを使うと楽ですが、Windows専用でもいいから細かい制御までしたい場合や、MFCなどのGUIフレームワークと連携したい場合はWGLを使ってレンダリング コンテキストを直に作成したりします。
例えば、GLUTのglutMainLoop()は一度呼び出すと制御を返さないので、終了するにはウィンドウのクローズ ボタンを押すか、exit()で強制終了させる以外になくなります。解放処理も記述できません。
他にも、GLEWを使うと、垂直同期やマルチサンプル アンチエイリアス、GLSLといった上位機能へのアクセスが楽になります。
OpenGLおよび関連ライブラリはほとんど全てがオープンソースでクロス プラットフォームなので、組込機器や技術論文などによく使われていますが、Windows限定であればDirect3Dのほうが圧倒的に情報量が多く、大抵の場合OpenGLより高速で、さらにC++との相性が良いですが、COMの知識が必須となります。
グラフィックAPIはすさまじく変化が激しいので、人に聞くよりも自ら情報収集に努めないと、はっきり言ってついていけないです。映画やゲーム、アニメなどを見れば一目瞭然ですが、この10年で飛躍的に進化を遂げた分野のうちのひとつです。
http://www.wakayama-u.ac.jp/~tokoi/opengl/libglu …
http://wisdom.sakura.ne.jp/system/opengl/index.h …
http://glew.sourceforge.net/
http://msdn.microsoft.com/en-us/library/ms533798 …
// No.1:
#include <cstdlib>
#include <GL/glut.h>
#include <Windows.h>
#include <GdiPlus.h>
#include <vector>
#include <cassert>
#pragma comment(lib, "gdiplus.lib")
using namespace Gdiplus;
template<typename T> inline bool IsModulo2(const T& x) { return !(x & (x - 1)); }
void DrawString(void* font, const char* str)
{
while (*str)
{
glutBitmapCharacter(font, *str);
++str;
}
}
No.3
- 回答日時:
// No.3 開始。
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static const LPCTSTR fileNames[BMP_NUM] =
{
_T("image00.bmp"),
_T("image01.bmp"),
_T("image02.bmp"),
_T("image03.bmp"),
_T("image04.bmp"),
_T("image05.bmp"),
_T("image06.bmp"),
_T("image07.bmp"),
};
switch (uMsg)
{
case WM_CREATE:
{
HDC hdc = ::GetDC(hWnd);
for (UINT i = 0; i < BMP_NUM; i++)
{
g_bmpHolders[i].LoadBitmapFromFile(fileNames[i], hdc);
}
RECT clientRect = {};
::GetClientRect(hWnd, &clientRect);
const int clientWidth = clientRect.right - clientRect.left;
const int clientHeight = clientRect.bottom - clientRect.top;
const bool isBackBufCreated = g_backBuffer.UpdateBackBufferBitmap(hdc, clientWidth, clientHeight);
assert(isBackBufCreated);
::ReleaseDC(hWnd, hdc);
}
return 0;
case WM_DESTROY:
::PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
switch (wParam)
{
case VK_ESCAPE:
::PostQuitMessage(0);
return 0;
default:
break;
}
}
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
// No.3 終了。
ちなみに Direct3D だとダブルバッファリングやモニタのリフレッシュレートとの垂直同期(VSync)は、ほぼ自動でやってくれます。
またまたソースを書いてくださってありがとうございます。
Direct3DというのはDirectXの一部(?)と聞きました。
OpenGLは少しやったことがあるのですが、Direct3Dについては何もわかりません。
OpenGLでも可能でしょうか。とは言ってもOpenGLもさわりだけしかやっておらずさわりで挫折しました。
OpenGLでも可能ならば、どのようにすればいいかヒントを頂けますでしょうか。
本当に何度もすみません。
よろしくお願いします。
No.2
- 回答日時:
// No.2 開始。
namespace
{
const UINT BMP_NUM = 8; // 画像の総数。
//const UINT FPS_LIMIT = 60;
const UINT FPS_LIMIT = 160; // 1000[ms] / 6.25[ms]
BmpHolder g_backBuffer;
BmpHolder g_bmpHolders[BMP_NUM];
UINT g_fpsValue;
// ブラシを使わずに単色で塗りつぶす。
void FillSolidRect(HDC hdc, COLORREF clr, const RECT* pRect)
{
::SetBkColor(hdc, clr);
::ExtTextOut(hdc, 0, 0, ETO_OPAQUE, pRect, NULL, 0, NULL);
}
void RenderMyObjects(HDC hdc, const RECT* pCanvasRect)
{
static UINT currentBmpIndex; // 現在の画像番号。
{
FillSolidRect(hdc, RGB(0, 128, 128), pCanvasRect);
g_bmpHolders[currentBmpIndex].Render(hdc, 30, 30);
static TCHAR message[256];
_stprintf_s(message, _T("%3u FPS"), g_fpsValue);
RECT rect = { 4, 4, 4, 4 };
::DrawText(hdc, message, _tcslen(message), &rect, DT_NOCLIP);
}
currentBmpIndex = (currentBmpIndex + 1) % BMP_NUM;
}
// アイドル時に描画する。
void OnIdle(HWND hWnd)
{
HDC hdc = ::GetDC(hWnd);
{
// 直接ウィンドウの DC に描画すると、描画の過程が見えてしまうのでフリッカー(ちらつき)が発生する。
// フリッカー防止のため、一旦バックバッファ(裏画面)用のビットマップに描画し、
// その後バックバッファをフロントバッファ(表画面)であるウィンドウに転送する。
RECT clientRect = {};
::GetClientRect(hWnd, &clientRect);
RenderMyObjects(g_backBuffer.GetMemDC(), &clientRect);
::BitBlt(hdc, 0, 0, g_backBuffer.GetWidth(), g_backBuffer.GetHeight(), g_backBuffer.GetMemDC(), 0, 0, SRCCOPY);
}
::ReleaseDC(hWnd, hdc);
}
}
// No.2 終了。
No.1
- 回答日時:
プログラム2のお礼で「ダブル バッファリング」に関して質問を受けたので、こちらに記載します。
インクルードとメイン関数は前回と同じです。
ちなみに画面のちらつきは、フレームレートというよりは描画途中の過程が表画面に見えてしまうことが原因です。実際、前回提示したコードでは、FPS表示部分は直接描画しているため、微妙にちらついているのが分かります。一旦裏画面に描画して、一気に表画面に転送するようにすると、ちらつきが見えなくなります。
// No.1 開始。
class BmpHolder
{
BITMAP m_bmpInfo;
HBITMAP m_hBitmap;
HDC m_hMemDC;
bool m_isBackBuffer;
public:
BmpHolder()
: m_bmpInfo()
, m_hBitmap()
, m_hMemDC()
, m_isBackBuffer()
{}
virtual ~BmpHolder()
{
this->Release();
}
LONG GetWidth() const { return m_bmpInfo.bmWidth; }
LONG GetHeight() const { return m_bmpInfo.bmHeight; }
HDC GetMemDC() const { return m_hMemDC; }
// 描画されるビットマップをファイルからロードする。
bool LoadBitmapFromFile(LPCTSTR pFileName, HDC hdc)
{
this->Release();
m_hBitmap = static_cast<HBITMAP>(::LoadImage(NULL, pFileName, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
assert(m_hBitmap != NULL);
if (m_hBitmap)
{
m_isBackBuffer = false;
::GetObject(m_hBitmap, sizeof(BITMAP), &m_bmpInfo);
m_hMemDC = ::CreateCompatibleDC(hdc);
assert(m_hMemDC != NULL);
::SelectObject(m_hMemDC, m_hBitmap);
return true;
}
else
{
return false;
}
}
// バックバッファ用のビットマップを更新する。
// ウィンドウの作成時・リサイズ時に明示的に呼び出すか、フレームの描画前に毎回呼び出す。
bool UpdateBackBufferBitmap(HDC hdc, int newWidth, int newHeight)
{
// 現在の幅・高さと同じバックバッファであれば更新しない。
if (this->GetWidth() == newWidth && this->GetHeight() == newHeight && m_isBackBuffer)
{
return true;
}
this->Release();
m_hBitmap = ::CreateCompatibleBitmap(hdc, newWidth, newHeight);
assert(m_hBitmap != NULL);
if (m_hBitmap)
{
m_isBackBuffer = true;
::GetObject(m_hBitmap, sizeof(BITMAP), &m_bmpInfo);
m_hMemDC = ::CreateCompatibleDC(hdc);
assert(m_hMemDC != NULL);
::SelectObject(m_hMemDC, m_hBitmap);
return true;
}
else
{
return false;
}
}
void Render(HDC hdc, int posX, int posY)
{
if (m_hMemDC)
{
::BitBlt(hdc, posX, posY, this->GetWidth(), this->GetHeight(), m_hMemDC, 0, 0, SRCCOPY);
}
}
void Release()
{
if (m_hMemDC)
{
::DeleteDC(m_hMemDC);
}
if (m_hBitmap)
{
::DeleteObject(m_hBitmap);
}
m_bmpInfo = BITMAP();
m_isBackBuffer = false;
}
};
// No.1 終了。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) 【VBA】写真の貼り付けコードがうまく機能しません。 5 2022/09/01 18:43
- その他(プログラミング・Web制作) uwscとWinShotを使いスクリーンショットを撮る 1 2022/06/30 21:15
- 英語 Thus, PTH may be used to promote bone regeneration 1 2022/07/26 08:15
- C言語・C++・C# カードシャッフルのブログラムを使ってc言語でブラックジャックをしたい 2 2022/04/12 15:13
- その他(プログラミング・Web制作) uwscのCHKING関数の画像の認識がうまくいかない。 1 2022/06/18 20:55
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# c言語の問題の説明、各所ごとに 5 2023/07/26 11:03
- Visual Basic(VBA) Vba LongPtrについて教えてください 2 2022/08/19 11:14
- C言語・C++・C# C++のcase文の書き方 4 2023/02/24 20:50
- C言語・C++・C# C言語階乗の総和を求める 2 2023/03/04 23:31
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
エディットボックスの背景の色...
-
ダイアログプログラムでEnterキ...
-
CTreeCtrlのCreate関数でエラー...
-
Win32APIでウィンドウを中央に...
-
Win32でシリアル通信
-
ウィンドウハンドルがメッセー...
-
キャレットの現在位置の表示 (W...
-
C言語win32api、エディットボッ...
-
プログラミングでエラーが出ま...
-
ファンクションキー制御?
-
C言語についての質問です
-
COMPORTマルチスレッドで例外発生
-
マイナスからプラスへ転じた時...
-
fgetsなどのときのstdinのバッ...
-
*をユーザーが入力した数字の数...
-
Enterキーを押されたら次の処理...
-
C言語での引数の省略方法
-
#define _CRT_SECURE_NO_WARNIN...
-
2÷3などの余りについて
-
visualstudio C# テキストボッ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
Win32APIでウィンドウを中央に...
-
オーバーレイみたいな処理がしたい
-
ダイアログプログラムでEnterキ...
-
キャレットの現在位置の表示 (W...
-
PeekMessageについて
-
ウィンドウ非表示に時に表示し...
-
C#のswitch文を簡略化したいの...
-
C言語win32api、エディットボッ...
-
音量調節
-
LoadImageを使用し、タイトルバ...
-
プログラミングでエラーが出ま...
-
エディットコントロール入力時...
-
ウィンドウハンドルがメッセー...
-
ボタンの色(WINAPI)
-
画像を表示したいのですが……
-
WINAPIでキーから文字列を入力...
-
エラー Run-Time Check Failur...
-
エディットボックスの背景の色...
-
Win32APIでアイコンファイルを...
-
WM_KEYDOWNでPrtScを捕まえる方...
おすすめ情報