2007年4月24日火曜日

全画面表示

スライドショーは全画面表示にしたいのだけど、やりかたがわからない。

ShowWindow(hWnd, SW_SHOWMAXIMIZED);

で全画面になると思っていたけど、そうならなかった。
画面上部のタイトルバーと、下部のメニューバーは表示されてしまう。
メニューバーの表示を消すのにメニューの登録コードを全部削ったらメニューの表示はされなくなった。でもたまにSIPの選択部分だけ表示されたりする
あと、メニューがないのでソフトキーの受け取り方も分からない....

2007年4月23日月曜日

UIを作る

スライドショーの動きは出来てきたので、ユーザーインターフェイスを作ろうと思う
とりあえずカーソルキーで表示を進めたり、戻したりが出来るようにする

カーソルキーの状態はWM_KEYDOWNメッセージで取得することが出来る
wParamに押されたキーのコードが入る
カーソルの真ん中のボタンのコードが分からなかったので押して調べてみたらPROCESSKEYの後にVK_RETURNが届いた
VK_RETURNを処理しとけばいいかな


#define VK_RETURN 0x0D
#define VK_LEFT 0x25
#define VK_UP 0x26
#define VK_RIGHT 0x27
#define VK_DOWN 0x28
#define VK_PROCESSKEY 0xE5

2007年4月22日日曜日

Athlon64X2を買ってきた

このまえのAthlonの値下げ以来気になっていた64X2を買った
Intelも値下げするそうだが、長年AMDを信奉してきたのでAthlonを買うことにした。AMDにはがんばって欲しい。と言っても買ったのは3800+で98000円だけど

マザーボードはASUS M2A-VM HDMI 12800円を買った。メモリは1GByte7000円位のノーブランド。
ハードディスク、DVDドライブ、筐体と電源は使ってないmATX用のがあったので、総額3万円くらいですんだよ!

と思ったら、手持ちの電源のコネクタがMBに合わない...いつのまにATX電源のpin数が20Pinから24Pinに変わっていたらしい。知らなかった
あわててPCショップに電源を買いに行ったら、20Pinから24Pinの変換ケーブルを売っていたので購入。610円で済んだ。家の近所にPCショップがあってよかった。値段もほぼ秋葉価格なので、最近はあ秋葉に行くこともなくなってしまった。

2007年4月19日木曜日

Threadを使う

写真の表示がスムーズに動くようになったので、写真の切り替えがもたつかないようにマルチスレッド化した。
写真の表示中に、裏で次の写真ファイルを読み込む処理をする。

Threadの開始は

HANDLE LoaderHandle = CreateThread(NULL,0,LoaderThreead,0,0,(LPDWORD)&ThreadID);

最初の NULL はセキュリティ属性、指定しないのでNULL
次はThreadのスタックサイズ、指定しないので0
3番目は実行する関数名、LoaderThreead
4番目は実行関数に渡すパラメータ、指定しないので0
5番目はThreadをすぐ実行するか?サスペンドした状態のThreadを作るか?を指定する。すぐに実行する場合は0を指定、CREATE_SUSPENDEDで停止状態のThreadが作られる
停止中のThreadはResumeThreadで再開できる。実行中のThreadをSuspendThreadで停止することも出来る

LoaderThreead関数が終了するとThreadも終了するが、CloseHandle()をしないとThreadのオブジェクトが残ってメモリリークとなる


マルチスレッド化したのでレンダリング処理(RenderThread)と写真のロード処理(LoaderThread)が独立して動くようになるので、2つのthread間で協調して動かせるようにEventを使った

具体的には、

  1. RenderThreadは次の写真が必要になるとLoaderThreadが写真のロード処理が完了するまでThreadを停止して待つ
  2. LoaderThreadは写真のロードが完了したことをRenderThreadに伝え、さらに次の写真を読み込む要求がRenderThreadから来るまでThreadを停止して待つ
  3. RenderThreadは写真の表示の合間に次の写真をロードするようにLoaderThreadに要求を出す


といった具合にお互いの状態を連絡しあって処理をする事を同期処理というらしい

この同期にEventという仕組みがOSに用意されている。Eventの説明は検索するといっぱいヒットするので省略

Eventを使うメリットは処理待ちの間Threadが停止するので、ポーリングするより余計なCPUパワーを消費しないで済むことだと思う


Eventを使用するにはCreateEventでイベントを作成してHANDLEを取得する

m_LoadRequest = CreateEvent(NULL, FALSE, FALSE, TEXT("LOADREQUEST"));


Threadと同様にハンドルをクローズする必要がある

CloseHandle(m_LoadRequest);


Eventを待つときは

WaitForSingleObject(m_LoadRequest,INFINITE);


Eventをセットするときは


SetEvent(m_LoadRequest);


今回はイベントを自動リセットとしたのでResetEventを呼ぶ必要は無い

ThreadとEvent関連のソース抜粋


bool m_threadEnd;
HANDLE m_RenderHandle;
HANDLE m_LoaderHandle;
HANDLE m_UpdateEvent;
HANDLE m_LoadCmplete;
HANDLE m_LoadRequest;

void OnCreate(HWND hWnd)
{
WORD ThreadID;

gFrame = 0;
gNextFrame = 0;
pictnow = NULL;
pictload = NULL;

m_LoadRequest = CreateEvent(NULL, FALSE, FALSE, TEXT("LOADREQUEST")); // 自動リセット reset状態
m_LoadCmplete = CreateEvent(NULL, FALSE, FALSE, TEXT("LOADEREVENT")); // 自動リセット reset状態
m_UpdateEvent = CreateEvent(NULL, FALSE, TRUE , TEXT("UPDATEEVENT")); // 自動リセット
m_LoaderHandle = CreateThread(NULL,0,LoaderThreead,0,0,(LPDWORD)&ThreadID);
m_RenderHandle = CreateThread(NULL,0,RenderThreead,0,0,(LPDWORD)&ThreadID);
SetThreadPriority(m_RenderHandle,THREAD_PRIORITY_TIME_CRITICAL);
}

void OnDestroy(HWND hWnd)
{
m_threadEnd = TRUE;
// 停止中のスレッドに終了を伝える為にeventをセットする
SetEvent(m_UpdateEvent);
SetEvent(m_LoadRequest);
// ローダースレッドが処理を完了するまで待つ
WaitForSingleObject(m_LoadCmplete,INFINITE);
CloseHandle(m_LoadCmplete);
CloseHandle(m_LoaderHandle);
CloseHandle(m_RenderHandle);
CloseHandle(m_UpdateEvent);
CloseHandle(m_LoadRequest);
}

DWORD WINAPI RenderThreead(LPVOID lpParameter)
{
int frame = -1;
int render_frame = -1;

while(m_threadEnd!=TRUE)
{
// 描画するフレームを決定
if(frame<gFrame)
{
// フレーム更新
frame = gFrame;
}
else
{
// 更新前だが次のフレームを用意しておく
frame = gFrame +1;
}


if(frame>render_frame)
{
render_frame = frame;
if((pictnow==NULL)||(pictnow->IsEnd(frame)))
{
SetThreadPriority(m_LoaderHandle,THREAD_PRIORITY_ABOVE_NORMAL);
WaitForSingleObject(m_LoadCmplete,INFINITE);
if(pictnow)
delete pictnow;
if(pictload==NULL)
break;
pictnow = pictload;
pictload = NULL;

// ローダーを再開する(再開前にスレッドの優先度を下げておく)
SetThreadPriority(m_LoaderHandle,THREAD_PRIORITY_IDLE);
SetEvent(m_LoadRequest);
}
pictnow->Draw(pScreen->m_Buf,pScreen->m_dspW,pScreen->m_dspH,render_frame);
}
else
{
WaitForSingleObject(m_UpdateEvent,INFINITE);
}
}
return 0;
}

DWORD WINAPI LoaderThreead(LPVOID lpParameter)
{
while(m_threadEnd!=TRUE)
{
// 写真のファイル名を取得
TCHAR *fname = FindFile();
if(fname ==NULL)
{
// 取得できなかった
// SendMessage();
pictload = NULL;
break;
}

// ファイルを読み込む
pictload = new CPicture();
if(!pictload->LoadPicture(fname,gNextFrame))
{
// ファイルが読めなかった
continue;
}

// 次のファイルの開始フレームを決定
gNextFrame = pictload->frame_end;

// RenderThread にロード完了したことを伝える
SetEvent(m_LoadCmplete);

// さらに次のファイルを読み込みを開始できるまで待つ
WaitForSingleObject(m_LoadRequest,INFINITE);

}

// スレッド終了待ちを解除するため
SetEvent(m_LoadCmplete);
return 0;
}

2007年4月16日月曜日

画面への描画

結局画面への描画はこんな感じになった




class CScreen
{
public:
CScreen(void);
~CScreen(void);

HDC m_DC;
HBITMAP m_Bmp;
PBYTE m_Buf;
int m_dspW;
int m_dspH;
int m_WidthByte;

void Blt(HDC hdc, int w, int h);

};

CScreen::CScreen(void)
{
HDC dc = GetDC(NULL);
m_DC = CreateCompatibleDC(dc);
ReleaseDC(NULL,dc);

m_dspW = 480;
m_dspH = 640;
m_WidthByte = (m_dspW*2+1)&0xfffffffe;
m_Buf = (PBYTE) new WORD[m_WidthByte*m_dspH];

}

CScreen::~CScreen(void)
{
DeleteObject(m_DC);
delete [] m_Buf;
}

void CScreen::Blt(HDC hdc, int w, int h)
{
HGDIOBJ oldObj;
m_Bmp = CreateBitmap(m_dspW, m_dspH, 1, 16, m_Buf);

oldObj = SelectObject(m_DC, m_Bmp);
BitBlt(hdc,0,0,w,h,m_DC,0,0,SRCCOPY);
SelectObject(m_DC, oldObj);
DeleteObject(m_Bmp);
}


m_bufをオフスクリーン用のバッファとして、予め画像データを作成しておく

とりあえず画面にバッファの内容を表示することは出来た。
しかし、バッファの画像処理操作は結構遅い、VCのコンパイルオプションでバッファセキュリティチェックを”いいえ”にするとある程度改善されるようだ

Windows Live for m... 入れた



Windows Live for Windows Mobile 入れてみた
3日でアンインストールした
メッセンジャーする相手がいなかった...
Posted by Picasa

2007年4月14日土曜日

SetBitmapBitsを使った

CreateCompatibleBitmapとSetBitmapBitsを使ってメモリ上に表示イメージを作成して表示デバイスにbltするようにしてみた。ものすごく遅い...なんか嫌になってきた

StretchBltを使わないように拡大縮小も自前で用意してみたつもりなんだけど余計遅くなっただけかも。

src_buf -> m_buf -> m_hbmp > hdc
と転送が繰り返されているように思う

src_buf -> m_buf -> hdc
ぐらいにしたい


bool CPicture::Draw(HDC hdc, RECT rc, int frame)
{
int w = rc.right - rc.left;
int h = rc.bottom - rc.top;

// 表示用のバッファ領域を作成する
if(m_hBmp == NULL)
{
m_hBmp = CreateCompatibleBitmap(hdc,w,h);

m_WidthByte = (w*2+1)&0xfffffffe;
m_buf = (LPBYTE) new WORD[m_WidthByte*h];
}

// イメージ内の表示領域を取得する
KEY src = GetValue(frame);

int srcX = src.sx;
int srcY = src.sy;
int srceX = src.ex;
int srceY = src.ey;
int srcW = src.ex - src.sx;
int srcH = src.ey - src.sy;


int ddx,ddy;
int sy = srcY;
unsigned short rgb;

// 拡大縮小とフェードインアウト画像処理
WORD *dpx;
ddy = h;
for(int y=0;y<h;y++)
{
int sidx = (m_Height-sy)*m_Width + srcX;
dpx = (WORD*)(m_WidthByte*y + m_buf);
ddx = w;
ddy -= srcH;
while(ddy<=0)
{
sy += 1;
ddy+= h;
}

for(int x=0;x<w;x++)
{
if(sidx<m_Width*m_Height)
{
unsigned char r = (src_buf[sidx].rgbRed * src.fader)>>8;
unsigned char g = (src_buf[sidx].rgbGreen * src.fader)>>8;
unsigned char b = (src_buf[sidx].rgbBlue * src.fader)>>8;
rgb = ((r << 8) & 0xf800)
| ((g << 3) & 0x07e0)
| ((b >> 3) & 0x001f);
}
else
{
rgb = 0;
}
*dpx=rgb;

dpx++;
ddx -= srcW;
while(ddx<=0)
{
sidx+=1;
ddx += w;
}
}
}
// バッファのイメージをbitmapにコピーする
SetBitmapBits(m_hBmp,m_WidthByte*h,m_buf);

// ディスプレイにbitmapをコピーする
HDC hBmpDC = CreateCompatibleDC(hdc);
SelectObject(hBmpDC, m_hBmp);
BitBlt(hdc,0,0,w,h,hBmpDC,0,0,SRCCOPY);
DeleteObject(hBmpDC);


return true;
}