OrangeMaker_logo
memo アイコン SDK-MFC 備忘録

Windows SDKやMFCに関するメモです。
(記載内容について正しいことを保証するものではありません。MSDN等で確認してください)

虫眼鏡ウィンドウの作り方

2009/10/25 作成
2010/2/11 記述が前後している部分を修正
2010/9/24 画像の転送タイミングについて注釈追記

 VISTAからMagnifictionという拡大鏡APIがあります。このAPIがちょっと変わっていて日本語の資料もあまり見当たらないのでわかった範囲でメモっておきます。

 MSDNの英語の資料で分かりずらいのはこの拡大鏡は特定の名前のウィンドウクラスのウィンドウを生成することから始まる点です。このウィンドウを生成したあとはこのウィンドウのハンドルを引数に倍率の設定と転送元の矩形の指定だけです。

1)まず最初に

Mag系をのAPIをつかうにはまず最初にMagInitialize();を1回呼んでおきます。引数はありません。

2)コンテナウィンドウ

 次に拡大鏡ウィンドウを乗せるコンテナウィンドウです。
拡大鏡を乗せるウィンドウの拡張ウィンドウスタイルに指定があります。拡張ウィンドウスタイルのWS_EX_LAYEREDをONします。

SDK的にはCreateWindowEx()するときに拡張ウィンドウスタイルを指定するかGetWindowLong()/SetWindowLong()でビットを立ててやる必要があります。

mfc的にはダイアログならOnInitDialog()あたりが無難なところでしょうか、要するにWindow生成後から表示するまでならどこでもかまわない思います。拡張ウィンドウスタイルはリソースで指定するのでもいいと思います。Layeredの指定を行うのでトップレベルのウィンドウでなければならい点が注意点です。

レイヤードウィンドウの指定を行った後に、SetLayeredWindwoAtriibute()で255のアルファ値の設定を行います。

SetLayeredWindowAttributes(hWnd, 0, 255, LWA_ALPHA);

3)拡大鏡ウィンドウの生成

 次に拡大鏡のウィンドウを生成します。ウィンドウクラスはWC_MAGNIFIERである必要があります。

コンテナウィンドウのクライアント領域全体を拡大鏡ウィンドウが占めるようにするには、

  HINSTANCE hInst = AfxGetInstanceHandle();
 CRect r;
 GetClientRect(&r);
 m_hwndMag = CreateWindowEx(0, WC_MAGNIFIER, TEXT("MagnifierWindow"), 
     WS_CHILD | MS_CLIPAROUNDCURSOR | WS_VISIBLE,r.left, r.top, r.right, 
 r.bottom, this->m_hWnd, NULL, hInst,NULL );
 if (!m_hwndMag){
     //error;
 }
		

m_hwndMagはHWND型のハンドルです。このハンドルが以降のAPIの引数になります。なのでダイアログならクラス変数等で保持っておきます。

ウィンドウスタイルのなかに見慣れないのがMS_CLIPAROUNDCURORですがここで指定するMAG関係のウィンドウスタイルは3つあります。

// Magnifier Window Styles
#define MS_SHOWMAGNIFIEDCURSOR 0x0001L
 マウスカーソルを含むキャプチャ行う。
#define MS_CLIPAROUNDCURSOR 0x0002L
 マウスカーソルを含まないキャプチャを行う
#define MS_INVERTCOLORS 0x0004L
 色反転したキャプチャを行う。
	

4)拡大率の指定

次に拡大率の指定を行います。使用するAPIは、MagSetWindowTransform()です。

  MAGTRANSFORM matrix;
 memset(&matrix, 0, sizeof(matrix));
 matrix.v[0][0] = m_fMagFactor;
 matrix.v[1][1] = m_fMagFactor;
 matrix.v[2][2] = 1.0f;
 BOOL ret = MagSetWindowTransform(m_hwndMag, &matrix);
		
m_fMagFactorがフロート型の変数で拡大率です。(2倍の場合は2.0f)
ここの第1引数のウィンドウハンドルは2)で生成した拡大鏡ウィンドウのウィンドウハンドルです。

5)転送元の指定
次に転送元の指定です。使用するAPIはMagSetWindowSource()です。

    RECT r;
 ・・
 ・・
  MagSetWindowSource(m_hwndMag, r);
  InvalidateRect(hwndMag, NULL, TRUE);

 rに拡大したいスクリーン座標を矩形で指定します。定番的にはマウス座標を含む領域を先の拡大鏡ウィンドウのウィンドウサイズの逆数のサイズを計算します。
たとえば、拡大率が2倍ならこの矩形の幅、高さは表示する拡大鏡ウィンドウのそれぞれ1/2のサイズになります。
ここも第1引数は、拡大鏡ウィンドウのハンドルです。

この処理をタイマーイベントなどでマウス移動に追従するように計算して更新すればいいようです。

※最後のinvalidateRect()をトリガに画像が画面に転送されます。画像がリアルタイムに表示するためにはこれを一定周期で実行する必要があります。
(2010/9/24追記)

6)後始末。

コンテナウィンドウの終了処理でMagUninitialize()を呼び出す必要があります。
あとは、Windowsのお作法どうり拡大鏡ウィンドウのDestoryWindow()しておきます。