GDI+を利用して、PNGファイルを読み込む


Visual Basic の LoadPicture 関数は、ビットマップ ファイル (.BMP)、アイコン ファイル (.ICO)、カーソル ファイル (.CUR)、run-length エンコード ファイル (.RLE)、メタファイル (.WMF)、拡張メタファイル (.EMF)、GIF ファイル (.GIF)、JPEG ファイル (.JPG) を読み込んで Picture オブジェクトを返す。
しかし、PNG(Portable Network Graphics) 形式の画像ファイルが読み込めない点が唯一の残念な所です。

GDI+ には PNG 形式の画像ファイルを利用する事もできるので、GDI+ を利用する事にします。
GDI+ の概要については、ここを参考にしてください。

GDI+ の実体は、gdiplus.dll でこの DLL に API が実装されています。
日本語版の MSDN では、.NET のマネージドコードの説明しかなく、 英語版では C++ を対象にしたドキュメントは充実していますが、 Visual Basic 6 で GDI+ を使われては Visual Basic .NET の価値が下がると恐れてか(どうかは知りませんが)、 Visual Basic 6 で使う為の資料は全くありません...。

後は、勘とテストあるのみ!(^_^;)

GDI+ を利用するためには、最初に GdiplusStartup 関数を呼んで初期化し、最後に GdiplusShutdown 関数でシャットダウンしなければならない。

GdiplusStartup は、MSDN によれば、

Status GdiplusStartup(
    ULONG_PTR token *token,
    const GdiplusStartupInput *input,
    GdiplusStartupOutput *output
);

とある。

パラメータは、

token [out]ULONG_PTR 型のポインタである、トークンを返す。 GDI+ の使用終了時に GdiplusShutdown 関数でトークンを使用する。
input [in]GdiplusStartupInput 構造体のポインタで、 GDI+ のバージョン、デバッグ用コールバック関数のポインタ、 バックグラウンド・スレッドで使用するかの Boolean の値、 外部のイメージ・コーデックを使用するかの Boolean の値をセットする。
output [out]GdiplusStartupOutput 構造体のポインタで、 通知用フック関数のポインタと通知用フック解除関数のポインタをセットする。
(略)
これらのパラメータが不要な場合は NULL でよい。

と英語で書かれています。(これは、私が適当に翻訳したもの)

GdiplusStartupInput 構造体については、

struct GdiplusStartupInput
{
    UINT32 GdiplusVersion;             
    DebugEventProc DebugEventCallback;
    BOOL SuppressBackgroundThread;
    BOOL SuppressExternalCodecs; 
    
    GdiplusStartupInput(
        DebugEventProc debugEventCallback = NULL,
        BOOL suppressBackgroundThread = FALSE,
        BOOL suppressExternalCodecs = FALSE)
    {
        GdiplusVersion = 1;
        DebugEventCallback = debugEventCallback;
        SuppressBackgroundThread = suppressBackgroundThread;
        SuppressExternalCodecs = suppressExternalCodecs;
    }
};
typedef VOID (WINAPI *DebugEventProc)(DebugEventLevel level, CHAR *message);

とあります。構造体の中に関数のようなものがありますが、 構造体のコンストラクタですよね?きっと。
初期値として、1, NULL, FLASE, FALSEが順番に入るようです。
今回はそんな高度な機能は使わない(使えない)ので、初期値を入れて使います。

GdiplusShutdown 関数は、MSDN によれば、

void GdiplusShutdown(
    ULONG_PTR token
);

とあるので、これらの関数を Visual Basic での宣言にすると、

Private Type GdiplusStartupInput
    GdiplusVersion              As Long
    DebugEventCallback          As Long
    SuppressBackgroundThread    As Long
    SuppressExternalCodecs      As Long
End Type
Private Declare Function GdiplusStartup Lib "GDIPlus" _
                                (ByRef token As Long, _
                                 ByRef inputbuf As GdiplusStartupInput, _
                                 ByRef outputbuf As Long) As Long
Private Declare Function GdiplusShutdown Lib "GDIPlus" _
                                (ByVal token As Long) As Long

となる。

今回は、PNG ファイルを読み込みたいので、GdipCreateBitmapFromFile 関数でイメージをファイルから読み込み(C++の)ビットマップオブジェクトに変換、 GdipCreateHBITMAPFromBitmap でビットマップハンドルに変換する。 不要になったビットマップオブジェクトは、GdipDisposeImage で開放する。

GdipCreateBitmapFromFileとGdipCreateHBITMAPFromBitmapとGdipDisposeImageは、MSDN によると、

GpStatus WINGDIPAPI GdipCreateBitmapFromFile(
    GDIPCONST WCHAR* filename, 
    GpBitmap **bitmap
);
GpStatus WINGDIPAPI GdipCreateHBITMAPFromBitmap(
    GpBitmap* bitmap, 
    HBITMAP* hbmReturn, 
    ARGB background
);
GpStatus WINGDIPAPI GdipDisposeImage(
    GpImage *image
);

とそれぞれあるので、

Private Declare Function GdipCreateBitmapFromFile Lib "GDIPlus" _
                                (ByVal filename As Long, _
                                 ByRef bitmap As Long) As Long
Private Declare Function GdipCreateHBITMAPFromBitmap Lib "GDIPlus" _
                                (ByVal bitmap As Long, _
                                 ByRef hbmReturn As Long, _
                                 ByVal background As Long) As Long
Private Declare Function GdipDisposeImage Lib "GDIPlus" _
                                (ByVal image As Long) As Long

とする。GdipCreateBitmapFromFile の第一パラメータは、WCHAR のポインタなので、 Visual Basic の隠しメソッドの StrPtr でファイル名を変換すれば良い。

ビットマップハンドルから Picture オブジェクトの変換は、 「ビットマップの灰色をシステムの3Dカラーに変換する」 での OleCreatePictureIndirect を利用する方法を使う。

で、実装としては、極力LoadPicture 関数と同じようにしたいので、

'
' PNGファイルを読み込んでPictureオブジェクトを返す。
'
Public Function LoadPNGPicture(ByVal strFileName As String) As Picture
    Dim gsi As GdiplusStartupInput
    Dim lngResult As Long
    Dim lngGdiPlusTolen As Long
    Dim lngBitmap As Long
    Dim hBitmap As Long

    ' 構造体初期化
    With gsi
        .GdiplusVersion = 1
        .DebugEventCallback = 0
        .SuppressBackgroundThread = 0
        .SuppressExternalCodecs = 0
    End With

    ' GDI+初期化
    lngResult = GdiplusStartup(lngGdiPlusTolen, gsi, 0)
    If lngResult = 0 Then
        ' イメージファイルの読み込み
        lngResult = GdipCreateBitmapFromFile(strPtr(strFileName), lngBitmap)
        If lngResult = 0 Then
            ' GDIビットマップハンドルを作成する。
            lngResult = GdipCreateHBITMAPFromBitmap(lngBitmap, hBitmap, 0)
            
            ' ピクチャーオブジェクトに変換する処理。
            Set LoadPNGPicture = ConvertBitmap(hBitmap)
            
            ' 取得したイメージを開放する。
            Call GdipDisposeImage(lngBitmap)
        End If
        Call GdiplusShutdown(lngGdiPlusTolen)
    End If
    If lngResult Then Err.Raise 481, , "ピクチャが不正です。"
End Function

使用方法は、

Private Sub Command1_Click()
    Dim strFileName As String
    
    On Error GoTo errHdr
    
    strFileName = "C:\Documents and Settings\heropa\My Documents\HEROPA\HomePage\www31.ocn.ne.jp\~heropa\picture\vbdefmf.png"
    Picture1.BorderStyle = 0    ' なし
    Picture1.AutoRedraw = True
    Set Picture1.Picture = LoadPNGPicture(strFileName)
    ' 読み込んだピクチャーオブジェクトにサイズを合わせる。
    Picture1.Width = Me.ScaleX(Picture1.Picture.Width, 8, Me.ScaleMode)
    Picture1.Height = Me.ScaleY(Picture1.Picture.Height, 8, Me.ScaleMode)

Exit Sub
errHdr:
    MsgBox Err.Description, vbExclamation
End Sub

の様に使う。
ちなみに、GDI+が対象とする画像ファイルは何でも読み込めてしまうが、気にしない気にしない。(^_^;)


Back Home