
柴犬の得意なカエル座りです。
今回の概要
ビットマップ画像の白い領域を透過にしてみることを考えてみました。
ビットマップファイルを読み込み、1 ピクセル毎に白であるか確認します。
比較は 1ピクセルのARGBが &HFFFFFFFF であるなら、&H0 に変換します。
これを PNG にエンコードして保存します。
次の画像は、この方法によりビットマップ test.bmp を test.png に変換したものです。
透過になっているのが確認できます。

大量なAPI関数群
使用したAPI関数です。
Option Explicit
'--------------------------------------------------
'------GDI+ API定義
Private Declare PtrSafe Function GdiplusStartup Lib "gdiplus" ( _
ByRef tokenPtr As LongPtr, _
ByRef inputPtr As GdiplusStartupInput, _
Optional ByVal outputPtr As LongPtr = 0) As Long
'------GdiplusStartupn のtokenPtrが渡される
Private Declare PtrSafe Sub GdiplusShutdown Lib "gdiplus" ( _
ByVal tokenPtr As LongPtr)
'------ファイルからImageオブジェクトを取得する
Private Declare PtrSafe Function GdipLoadImageFromFile Lib "gdiplus" ( _
ByVal FileNamePtr As LongPtr, _
ByRef imagePtr As LongPtr) As Long
Private Declare PtrSafe Function GdipDisposeImage Lib "gdiplus" ( _
ByVal imagePtr As LongPtr) As Long
Private Declare PtrSafe Function GdipSaveImageToFile Lib "gdiplus.dll" ( _
ByVal imagePtr As LongPtr, _
ByVal fileName As LongPtr, _
ByRef clsidEncoder As UUID, _
ByVal encoderParams As LongPtr) As Long
Private Declare PtrSafe Function CLSIDFromString Lib "ole32" ( _
ByVal lpszCLSID As LongPtr, _
ByRef pclsid As UUID) As Long
Private Declare PtrSafe Function GdipGetImageWidth Lib "gdiplus" ( _
ByVal imagePtr As LongPtr, _
ByRef width As Long) As Long
Private Declare PtrSafe Function GdipGetImageHeight Lib "gdiplus" ( _
ByVal imagePtr As LongPtr, _
ByRef height As Long) As Long
Private Declare PtrSafe Function GdipBitmapGetPixel Lib "gdiplus" ( _
ByVal imagePtr As LongPtr, _
ByVal x As Long, _
ByVal y As Long, _
ByRef colorARGB As Long) As Long
Private Declare PtrSafe Function GdipBitmapSetPixel Lib "gdiplus" ( _
ByVal imagePtr As LongPtr, _
ByVal x As Long, _
ByVal y As Long, _
ByVal colorARGB As Long) As Long
Private Type UUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(7) As Byte
End Type
'------GdiplusStartup構造体
Private Type GdiplusStartupInput
GdiplusVersion As Long 'GDI+ のバージョン 1 を指定
DebugEventCallback As LongPtr '既定値は 0
SuppressBackgroundThread As Long '既定値は 0
SuppressExternalCodecs As Long '既定値は 0
End Type
Private Type EncoderParameter
Guid As UUID
NumberOfValues As Long
TypeAPI As Long
Value As LongPtr
End Type
Private Type EncoderParameters
Count As Long
Parameter(15) As EncoderParameter
End Type
変換は簡単、保存が大変
変換は、x と y の For ループのところで行っています。
GetCLSID 関数については、こういうこともできるのだと考えさせられました。
'--------------------------------------------------
'画像を読み込んで1ピクセルを1セルで描画する
'対応フォーマット:GDI+準拠(BMP, JPEG, GIF, TIFF, PNG)
Public Function ImageConverter(ByVal strInName As String, _
ByVal strOutName As String, _
ByVal sFormat As String) As Boolean
Dim myGdiStartupInput As GdiplusStartupInput
Dim myEncoderParam As EncoderParameters
Dim myToken As LongPtr
Dim hBitmap As LongPtr
Dim width As Long
Dim height As Long
Dim Quality As Long
Dim myARGB As Long
Dim myEncode As String
Dim x As Integer
Dim y As Integer
Dim RES As Boolean
Const CLSID_BMP As String = _
"{557CF400-1A04-11D3-9A73-0000F81EF32E}"
Const CLSID_GIF As String = _
"{557CF402-1A04-11D3-9A73-0000F81EF32E}"
Const CLSID_TIF As String = _
"{557CF405-1A04-11D3-9A73-0000F81EF32E}"
Const CLSID_PNG As String = _
"{557CF406-1A04-11D3-9A73-0000F81EF32E}"
Const CLSID_JPEG As String = _
"{557CF401-1A04-11D3-9A73-0000F81EF32E}"
Const CLSID_QUALITY As String = _
"{1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}"
Select Case sFormat
Case "JPG"
myEncode = CLSID_JPEG
Case "GIF"
myEncode = CLSID_GIF
Case "TIF"
myEncode = CLSID_TIF
Case "PNG"
myEncode = CLSID_PNG
Case Else
myEncode = CLSID_BMP
End Select
'------GDI+ のバージョン 1 を指定
' 他は規定値
myGdiStartupInput.GdiplusVersion = 1
'------戻り値 Status 列挙体 成功 0 失敗 0以外
If GdiplusStartup(myToken, myGdiStartupInput) <> 0 Then
GoTo ERRSHORI
End If
'------画像読み込み 戻り値 GpStatus 列挙体(Status 列挙体と同じ)
If GdipLoadImageFromFile(ByVal StrPtr(strInName), hBitmap) <> 0 Then
GoTo ERRSHORI
End If
'------幅取得 戻り値 GpStatus 列挙体
If GdipGetImageWidth(hBitmap, width) <> 0 Then
GoTo ERRSHORI
End If
'------高さ取得 戻り値 GpStatus 列挙体
If GdipGetImageHeight(hBitmap, height) <> 0 Then
GoTo ERRSHORI
End If
For y = 0 To height - 1
For x = 0 To width - 1
'------ピクセルの取得 戻り値 GpStatus 列挙体
If GdipBitmapGetPixel(hBitmap, x, y, myARGB) <> 0 Then
GoTo ERRSHORI
End If
'------ピクセルの変換です。
If myARGB = &HFFFFFFFF Then
Call GdipBitmapSetPixel(hBitmap, x, y, &H0)
End If
Next x
Next y
If sFormat = "JPEG" Then
'------JPEGのクオリティー設定
Quality = 90
'------JPEG用
myEncoderParam.Count = 1
With myEncoderParam.Parameter(0)
.Guid = GetCLSID(CLSID_QUALITY)
.NumberOfValues = 1
.TypeAPI = 4
.Value = VarPtr(Quality)
End With
Call GdipSaveImageToFile(hBitmap, _
StrPtr(strOutName), _
GetCLSID(myEncode), _
VarPtr(myEncoderParam))
Else
Call GdipSaveImageToFile(hBitmap, _
StrPtr(strOutName), _
GetCLSID(myEncode), _
ByVal 0&)
End If
Call GdipDisposeImage(hBitmap)
Call GdiplusShutdown(myToken)
RES = True
OWARI:
ImageConverter = RES
Exit Function
ERRSHORI:
Call GdiplusShutdown(myToken)
RES = False
MsgBox "エラーが発生"
Resume OWARI
End Function
'--------------------------------------------------
'文字列から CLSID を取得する
Private Function GetCLSID(ByVal S As String) As UUID
Dim RES As Long
RES = CLSIDFromString(StrPtr(S), GetCLSID)
End Function
EXCELのシートから実行しました
エクセルのシートのセルをダブルクリックすると実行します。
'--------------------------------------------------
'
Private Sub Worksheet_BeforeDoubleClick(ByVal Target As Range, _
Cancel As Boolean)
Cancel = True
Call MakeImage
End Sub
'--------------------------------------------------
'
Private Sub MakeImage()
Dim inImage As String
Dim outImage As String
inImage = "C:\Users\sora\Desktop\test.bmp"
outImage = "C:\Users\sora\Desktop\test.png"
If ImageConverter(inImage, outImage, "PNG") Then
MsgBox "完了"
End If
End Sub
GetCLSID 関数の疑問
Private Function GetCLSID(ByVal S As String) As UUID
Dim RES As Long
RES = CLSIDFromString(StrPtr(S), GetCLSID)
End Function
CLSIDFromString(StrPtr(S), GetCLSID) の解釈
GetCLSID関数の中にこれが、ぱっと見、GetCLSID が入れ子になっているので再帰?と考えがよぎりました。
でも、違う。
表題のことについて、私自身かなり考え込みました。
Microsoft のCLSIDFromString 関数の解説
HRESULT CLSIDFromString(
[in] LPCOLESTR lpsz,
[out] LPCLSID pclsid
);
これを読んで自分なりのまとめは次のようになりました。
GetCLSID が第二引数の位置にあるのは、StrPtr(S) を処理した結果を関数が返しているだけである。
つまり、
RES = CLSIDFromString(StrPtr(S), GetCLSID)
は
Dim output As Variant
RES = CLSIDFromString(StrPtr(S), output)
GetCLSID = output
である。
確信
先ほどの考えを実践してみました。
EXCEL で次のコードを考えてみました。

早速、これを実行してみました。
結果、何らエラーも出ることなく 4 がメッセージで示されました。
疑問の推測が正しいことが分かりました。
