二枚の画像をピクセルブレンドで切り替える

二枚の画像をピクセルブレンドしながら滑らかに切り替えます。
ブレンドは画像1と画像2のイメージデータへのポインタを DirectX の LockRect で取得して b,g,r を Rate に従って混ぜ合わせます。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. 二枚の画像をピクセルブレンドしながら、画像1から画像2に滑らかに切り替えます。
    このプログラムは LPDIRECT3DSURFACE9 のイメージ領域をロックして、直接画像をブレンドしています。
    g_pBackBuffer は Back Buffer の Surface です。
    g_pSurface1 は、一枚目の画像を入力する Surface です。
    g_pSurface2 は、二枚目の画像を入力する Surface です。
    g_pSurface は、二枚の画像をブレンドした画像を生成する Surface です。
    File1[], File2[] はブレンドする JPEG 画像ファイルの名前です。
    Rate はブレンドの度合いで 0.0 で Img1 の画像に、1.0 で Img2 の画像になります。
        /*****************************************/
        /*  二枚の画像をブレンドする    前田 稔  */
        /*****************************************/
        #define     NAME        "Blend"
        #include    "MyD3D.h"
        #define     S_WIDTH     480     //Surface 幅
        #define     S_HEIGHT    240     //Surface 高さ
    
        // Global Area
        HWND                g_hWnd;
        MyD3D               *myd3d        = NULL;   // MyD3D Object Class
        LPDIRECT3DDEVICE9   g_pDEV        = NULL;   // 描写用デバイス
        LPDIRECT3DSURFACE9  g_pBackBuffer = NULL;   // バックバッファ用
        LPDIRECT3DSURFACE9  g_pSurface1   = NULL;   // 画像1
        LPDIRECT3DSURFACE9  g_pSurface2   = NULL;   // 画像2
        LPDIRECT3DSURFACE9  g_pSurface    = NULL;   // 生成画像
        char    File1[]= "c:\\data\\ffx2s.jpg";     // 480*240
        char    File2[]= "c:\\data\\GeoS.jpg";      // 480*240
        float   Rate;                               // Blend のレート
        
  2. WinMain() では、画像のサイズに合わせてウインドウを設定しています。
    ウインドウのサイズが小さいと「全く画像が表示されない」ので注意して下さい。
        //★ Win Main
        int WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int)
        {   MSG     msg;
                :
            g_hWnd = CreateWindowEx(0,NAME,NAME,WS_OVERLAPPEDWINDOW,
                                    100,100,S_WIDTH+60,S_HEIGHT+60,
                                    NULL,NULL,hInst,NULL);
            if (InitGraphics()==FALSE)
            {   Cleanup();
                return FALSE;
            }
                :
            while(msg.message!=WM_QUIT)
            {   if (PeekMessage(&msg,NULL,0U,0U,PM_REMOVE))
                {   TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
                else    Render();
            }
                :
        
  3. DirectX Graphics を初期化する関数 InitGraphics() です。
    PathCheck() で画像ファイルのパスを確認しています。
    myd3d を生成して DirectX のデバイスを取得します。
    二枚の画像を入力する Surface とブレンドした画像を格納する Surface を生成します。
    一枚目はエラーの確認を行っていますが、二枚目からは同じ要領なのでエラーチェックを省略しました。
    二枚の画像を Surface に入力します。
    画像ファイルのパスを事前に確認しているので、エラーチェックを省略しました。
        // DirectX Graphics 初期化
        HRESULT  InitGraphics(void)
        {   HRESULT  hr;
    
            if (PathCheck(File1)==-1)
            {   MessageBox(NULL,"ファイルが見つかりません",File1,MB_OK);
                return FALSE;
            }
            if (PathCheck(File2)==-1)
            {   MessageBox(NULL,"ファイルが見つかりません",File2,MB_OK);
                return FALSE;
            }
            myd3d= new MyD3D(g_hWnd);
            myd3d->InitD3D(&g_pDEV);
            //Back Buffer を取得
            hr= g_pDEV->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&g_pBackBuffer);
            if (FAILED(hr))
            {   MessageBox(NULL,"GetBackBuffer Error","Error Message", MB_OK);
                return FALSE;
            }
            //OffscreenSurface の作成(二枚目からはエラーチェック無し)
            if (FAILED(g_pDEV->CreateOffscreenPlainSurface(S_WIDTH,S_HEIGHT,D3DFMT_X8R8G8B8,
                               D3DPOOL_DEFAULT,&g_pSurface,NULL)))
            {   MessageBox(NULL,"OffscreenSurface の作成に失敗","Error Message", MB_OK);
                return FALSE;
            }
            g_pDEV->CreateOffscreenPlainSurface(S_WIDTH,S_HEIGHT,D3DFMT_X8R8G8B8,
                    D3DPOOL_DEFAULT,&g_pSurface1,NULL);
            g_pDEV->CreateOffscreenPlainSurface(S_WIDTH,S_HEIGHT,D3DFMT_X8R8G8B8,
                    D3DPOOL_DEFAULT,&g_pSurface2,NULL);
            //画像ファイルの読み込み(パスをチェックしているのでエラーは無視)
            D3DXLoadSurfaceFromFile(g_pSurface1,NULL,NULL,File1,NULL,D3DX_FILTER_NONE,0,NULL);
            D3DXLoadSurfaceFromFile(g_pSurface2,NULL,NULL,File2,NULL,D3DX_FILTER_NONE,0,NULL);
            return TRUE;
        }
        
  4. 二枚の画像をブレンドしながら描画する Render() 関数です。
    timeGetTime() でブレンド係数(0.0~1.0)を設定します。
    Blend() 関数で二枚の画像をブレンドして g_pSurface に生成します。
    StretchRect() で g_pSurface の画像を BackBuffer に転送します。
        // OffscreenSurface の画像をブレンドして描画
        void  Render()
        {   RECT  Rect;
    
            Rate= float((timeGetTime()/50)%100)/100.0f;
            Blend(Rate);
            // バックバッファへ描画
            SetRect(&Rect,0,0,S_WIDTH,S_HEIGHT);
            g_pDEV->StretchRect(g_pSurface,&Rect,g_pBackBuffer,&Rect,D3DTEXF_NONE);
            g_pDEV->Present(NULL,NULL,NULL,NULL);
        }
        
  5. このプログラムのテーマである、二枚の画像をブレンドする関数です。
    g_pSurface1 と g_pSurface2 の領域を読み出し専用でロックして、その領域を取得します。
    g_pSurface の領域を書き込み用でロックして、その領域を D_Rect に取得します。
    D_Rect.Pitch には1ドットラインのバイト数が格納されています。
    1ドットは4バイトで構成され「b,g,r,a」の順に並んでいます。
    画像1と画像2の b,g,r を Rate に従ってブレンドして g_pSurface に格納します。
    画像が生成できれば、ロックした領域を解除して下さい。
        HRESULT  Blend(float Rate)
        {   D3DLOCKED_RECT  D_Rect,S_Rect1,S_Rect2;
            RECT            Rect;
            int             xp,yp;
            BYTE            r1,g1,b1,r2,g2,b2;
    
            SetRect(&Rect,0,0,S_WIDTH,S_HEIGHT);
            if (g_pSurface->LockRect(&D_Rect,&Rect,D3DLOCK_DISCARD)!=D3D_OK)
            {   MessageBox(NULL,"LockRect Error","Surface Error",MB_OK);
                return FALSE;
            }
            if (g_pSurface1->LockRect(&S_Rect1,&Rect,D3DLOCK_READONLY)!=D3D_OK)
            {   MessageBox(NULL,"LockRect Error","Surface Error",MB_OK);
                return FALSE;
            }
            if (g_pSurface2->LockRect(&S_Rect2,&Rect,D3DLOCK_READONLY)!=D3D_OK)
            {   MessageBox(NULL,"LockRect Error","Surface Error",MB_OK);
                return FALSE;
            }
            // r,g,b をブレンドして転送
            for(yp=0; yp<S_HEIGHT; yp++)
                for(xp=0; xp<D_Rect.Pitch; xp+=4)
                {   b1= *((BYTE*)S_Rect1.pBits+yp*D_Rect.Pitch+xp);
                    g1= *((BYTE*)S_Rect1.pBits+yp*D_Rect.Pitch+xp+1);
                    r1= *((BYTE*)S_Rect1.pBits+yp*D_Rect.Pitch+xp+2);
                    b2= *((BYTE*)S_Rect2.pBits+yp*D_Rect.Pitch+xp);
                    g2= *((BYTE*)S_Rect2.pBits+yp*D_Rect.Pitch+xp+1);
                    r2= *((BYTE*)S_Rect2.pBits+yp*D_Rect.Pitch+xp+2);
                    *((BYTE*)D_Rect.pBits+yp*D_Rect.Pitch+xp)=   (BYTE)(b1*(1.0f-Rate) + b2*Rate);
                    *((BYTE*)D_Rect.pBits+yp*D_Rect.Pitch+xp+1)= (BYTE)(g1*(1.0f-Rate) + g2*Rate);
                    *((BYTE*)D_Rect.pBits+yp*D_Rect.Pitch+xp+2)= (BYTE)(r1*(1.0f-Rate) + r2*Rate);
                }
    
            g_pSurface->UnlockRect();
            g_pSurface1->UnlockRect();
            g_pSurface2->UnlockRect();
            return TRUE;
        }
        

超初心者の方のために全ソースコードを掲載します。 (^_^;)
全ソースコード

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(DirectX9 game program)

超初心者のプログラム入門(DirectX10 game program)