トップ回答者
Windows7のAero でのAlphaBlend関数の挙動について

質問
-
Windows7環境のVisualBasic6.0(SP6)でAlphaBlend関数を使った半透明と完全透過を同時に行うビットマップ画像合成表示プログラムを作っています。デスクトップ表示モードがクラシックUIのときは半透明と完全透過を同時に行うことが出来たのですが、Aeroにした場合、半透明処理しか行われません。AlphaBlend関数の戻り値はどちらも正常です。
デスクトップ表示モードがAeroのAlphaBlend関数で半透明と完全透過を同時に行うことは出来ないのでしょうか。デスクトップ表示モードをクラシックUIにすることで問題を回避することも出来ますが、Windows8環境ではクラシックUIの切り替え選択肢もありません。解決策をご教示頂けますでしょうか。
以下はAlphaBlend関数呼び出しのVB6コードで、AlphaBlend関数のパラメータBlendFunction構造体はAlphaFormat=1、SourceConstantAlpha=128として、32bitDIB(αRGB値)で完全透過色ピクセルのαを0(完全透過)、それ以外の色のαを255(不透明:SourceConstantAlpha=128なので結果は半透明)としています。
Dim Bld As BlendFunction
Dim LngBld As LongBld.BlendOp = 0 'AC_SRC_OVER Bld.BlendFlags = 0 '常に0 Bld.SourceConstantAlpha = 128 '半透明 Bld.AlphaFormat = 1 '32bitBMPのαBGR値(完全透過ピクセルではα=0、それ以外は255) MoveMemory LngBld, Bld, Len(LngBld) Ret = AlphaBlend(PictureBox1.hDC, dstX, dstY, dstW, dstH, myBMP.hMemDC, SrcX, SrcY, SrcW, SrcH, LngBld)
回答
-
だから繰り返し言いますが、GetDIBits()/SetDIBits()を使うのをやめましょう。
第1引数に指定しているのはピクチャーボックスの互換メモリDCですよね? つまりDDBを経由してしまっています。結局肝心な部分は何も変わっていません。私が提示した、MSDNライブラリのサンプルコードでは、GetDIBits()/SetDIBits()は使わず、DIBのBGRAカラーバッファをpvBits経由で直接書き換えています。私の説明をきちんと読んだ上で、さらにサンプルコードを隅々まで読んで意味を理解した後でコードを書くようにしてください。料理と同様で、慣れないうちから自分で勝手にレシピにないアレンジを加えると失敗するのがプログラミングです。
なお旧VBでメモリーブロックへのポインタを経由して配列として扱う方法はWebを探せばいくらでも見つかるので説明はそちらに譲りますが、旧VBよりもVC++、VB.NETやVC#のほうが楽です。旧VBはできるかぎり早く捨てましょう。
Using DIB Sections in VB
すべての返信
-
質問の意図がいまいちつかめませんが、Per-Pixelのアルファチャンネルと、AlphaBlend()関数に与えるBLENDFUNCTION構造体のSourceConstantAlphaを合成したいということでしょうか?
Windows 8.1環境(当然Aero)でVisual Studio 2013のWin32/MFCを使って試してみましたが、普通に下記計算式のとおりに合成できると思います。
BLENDFUNCTION structure (Windows)
転送元(Src)となるDIBビットマップのほうを、CreateDIBSection()などを使ってきちんと32bit BGRAフォーマットで作成していますか? また、転送先(Dst)となるピクチャーボックスのDDBを、まず完全不透明色で塗りつぶしていますか?
Alpha Blending a Bitmap (Windows)
質問するときは前後の文脈が分かるように、過不足なく情報を公開しましょう。
なお、アルファブレンドを多用するのであれば、ハードウェアアクセラレートの効くWPF(VC#/VB.NET)やDirect2Dに移行したほうが良いと思います。
-
syghさま
アドバイスありがとうございます。
Visual Studio 2013(Win32/MFC)Windows8.1環境での動作検証のほうもありがとうございます。ということは、当方のDIBビットマップの作成に問題があるかもしれません。転送先(Dst)となるピクチャーボックスのDDBは完全不透明色です。転送元(Src)ですが、以下のコードのようになっています。CreateDIBSectionは使っていません。何かお気づきの点がございましたら、アドバイス頂けると幸いです。こちらでも、もう少し調べてみたいと思います。
'メモリデバイスコンテキスト取得
hMemDC = CreateCompatibleDC(ピクチャーボックスのhDC) 'ビットマップハンドル取得 hBitmap = CreateCompatibleBitmap(ピクチャーボックスのhDC, Width, Height) '32bitBMP配列確保 Redim BmpImg (Width * Height - 1) 'メモリデバイスコンテキストにビットマップを選択 Ret = SelectObject(hMemDC, hBitmap) '空のビットマップにGetDIBitsで空のビットマップデータを格納 'DIBセクションの設定 BmpInf.bmiHeader.biSize = 40 BmpInf.bmiHeader.biWidth = Width BmpInf.bmiHeader.biHeight = Height BmpInf.bmiHeader.biPlanes = 1 BmpInf.bmiHeader.biBitCount = 32 Ret = GetDIBits(hMemDC, hBitmap, 0, Height, .BmpImg(0), BmpInf, 0) ’ここから32bitBMP配列にBGRA値を書き込み~~~~~ BmpImg(ピクセル座標) = BGRA値 '~~~~~~~~~~~~~~~~~~~~ここまで 'メモリデバイスコンテキストに変更されたビットマップを反映 Ret = SetDIBits(hMemDC, hBitmap, 0, Height, BmpImg(0), BmpInf, 0) 'これ以降が、AlphaBlend関数の呼び出し処理になります。
- 編集済み Iwa55 2014年9月23日 12:00
-
CreateCompatibleBitmap()で作成されるのはDIBではなくDDBです。MSDNの説明にも、「デバイスと互換性のあるビットマップを作成する」と説明されています。
DDBはWindowsの画面モード(色深度)などによっても内部フォーマットが変動しうるので、CreateCompatibleBitmap()で生成したDDBが32bit BGRAフォーマットであることを期待するのはNGです。なお、SetDIBits()はDIBデータをDDBに転送するための関数で、転送元DIBと転送先DDBの内部フォーマットの違いを吸収します。
私が前回提示した、「Alpha Blending a Bitmap」に、CreateDIBSection()を使ったC/C++向けの完全なサンプルコードがそっくりそのまま載っているので、CreateCompatibleBitmap()で作成したDDBをアルファブレンドの転送元に使うのはやめて、まずはそちらを読んでください。
なお、32bit DIBのカラーチャンネルの並びは、BGRA順になります。リトルエンディアン表記で言えばARGBになります。
ちなみに、繰り返しになりますが、旧VBを使うのはそろそろやめて、VB.NET/VC#+WPFに移行したほうがいいと思います(VBAもVSTOに移行)。.NETやWPFに慣れてしまえば、わざわざ旧VBでWin32 APIを叩くよりもはるかに効率的に開発できます。また、Vista以降でグラフィックスアクセラレーターを活用した、スムーズで応答性の高いUIを構築できるのはWPFやDirect2D/Direct3Dだけです。GDI自体はまだ廃止されることはないと思いますが、すでにメインストリームではなく、旧VBやVBA同様に今後どんどん隅に追いやられていく運命にあるレガシーAPIです。
- 編集済み sygh 2014年9月23日 16:55
-
syghさま
連絡が遅くなり失礼しました。
CreateCompatibleBitmap()で作成されるのがDIBではなくDDBという事には気づきませんでした。大変勉強になりました。
ご教示頂いた、「Alpha Blending a Bitmap」のC/C++コードを参考にVB6コードを修正してみたいと思います。
VB.NET/VC# + WPFでの開発をお勧めされる理由もわかりますが、10年ほど前から積み上げたVB6コードが膨大なため、いまだに移行をためらっております。また、オブジェクト指向プログラミングについて理解が半端であることも理由の一つです。しかし、初心に戻って「習うより慣れろ」の精神で一から始めてみる気持ちが少し湧いてきました。
とりあえず、VB6での修正コードの動作確認ができましたら、少し遅れますが、ご報告したいと思います。
-
CreateCompatibleBitmap()をCreateDIBSection()に変更して動作確認をしてみましたが、全く同じ結果でした。
正方形の左上側を完全透過にしたいのですが、以下のイメージのとおり、Aeroでは背景の色と混ぜた色(=NG)になってしまいます。クラシックUIでは完全透過(=OK)になっております。今回テストした完全なVB6コードも載せますので、よろしければアドバイス頂けないでしょうか。
'------------------------------------------------------------- 'フォームモジュール(Form1.frm) '------------------------------------------------------------- VERSION 5.00 Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.1#0"; "MSCOMCTL.OCX" Begin VB.Form Form1 Caption = "AlphaBlendのテスト" ClientHeight = 2400 ClientLeft = 120 ClientTop = 450 ClientWidth = 4560 LinkTopic = "Form1" ScaleHeight = 2400 ScaleWidth = 4560 StartUpPosition = 3 'Windows の既定値 Begin VB.CheckBox chkTrns Caption = "透過あり" Height = 225 Left = 3450 TabIndex = 6 Top = 1020 Width = 975 End Begin MSComctlLib.Slider sld Height = 345 Left = 1800 TabIndex = 3 Top = 930 Width = 1275 _ExtentX = 2249 _ExtentY = 609 _Version = 393216 BorderStyle = 1 LargeChange = 15 Max = 255 SelStart = 255 TickFrequency = 15 Value = 255 End Begin VB.OptionButton Opt Caption = "CreateCompatibleBitmap使用" Height = 315 Index = 1 Left = 1800 TabIndex = 2 Top = 480 Width = 2655 End Begin VB.OptionButton Opt Caption = "CreateDIBSection使用" Height = 315 Index = 0 Left = 1800 TabIndex = 1 Top = 120 Value = -1 'True Width = 2655 End Begin VB.PictureBox Pic AutoSize = -1 'True Height = 1560 Left = 90 Picture = "Form1.frx":0000 ScaleHeight = 100 ScaleMode = 3 'ピクセル ScaleWidth = 100 TabIndex = 0 Top = 90 Width = 1560 End Begin VB.Label lbl Caption = "青の正方形を半透明と完全透過で合成" Height = 345 Left = 150 TabIndex = 7 Top = 1860 Width = 4155 End Begin VB.Label Label2 Caption = "透明" Height = 195 Left = 1740 TabIndex = 5 Top = 1380 Width = 405 End Begin VB.Label Label1 Caption = "不透明" Height = 195 Left = 2580 TabIndex = 4 Top = 1380 Width = 585 End End Attribute VB_Name = "Form1" Attribute VB_GlobalNameSpace = False Attribute VB_Creatable = False Attribute VB_PredeclaredId = True Attribute VB_Exposed = False Option Explicit Option Base 0 Private Sub chkTrns_Click() sld_Change End Sub Private Sub sld_Change() Const DIB_RGB_COLORS = 0 Const AC_SRC_OVER = 0 Const AC_SRC_ALPHA = 1 Dim BmpInf As BITMAPINFO Dim pvBits As Long 'CreateDIBSectionのbitmap配列ポインタ Dim hMemDC As Long, hBitmap As Long Dim Ret As Long Dim BMPArray() As Long 'ビットマップ編集バッファ Dim biW As Long, biH As Long Dim X As Long, Y As Long Dim ARGB As Long Dim Bld As BlendFunction Dim LngBld As Long Dim Alpha As Byte Pic.Cls biW = Pic.ScaleWidth biH = Pic.ScaleHeight 'メモリデバイスコンテキストのハンドル取得 hMemDC = CreateCompatibleDC(Pic.hDC) 'DIBセクション設定 BmpInf.bmiHeader.biSize = LenB(BmpInf.bmiHeader) BmpInf.bmiHeader.biWidth = biW BmpInf.bmiHeader.biHeight = biH BmpInf.bmiHeader.biPlanes = 1 BmpInf.bmiHeader.biBitCount = 32 If Opt(0).Value = True Then 'ビットマップのハンドル取得(CreateDIBSection版) hBitmap = CreateDIBSection(Pic.hDC, BmpInf, DIB_RGB_COLORS, pvBits, 0, 0) Else 'ビットマップのハンドル取得(CreateCompatibleBitmap版) hBitmap = CreateCompatibleBitmap(Pic.hDC, biW, biH) End If 'メモリデバイスコンテキストにビットマップを選択 Ret = SelectObject(hMemDC, hBitmap) '指定されたビットマップのビットを取得し、ビットマップ編集バッファへコピー ReDim BMPArray(biW * biH - 1) Ret = GetDIBits(hMemDC, hBitmap, 0, biH, BMPArray(0), BmpInf, 0) '正方形の対角線を境に半透明と完全透過の青色ビットマップを編集 For Y = 0 To biH - 1 For X = 0 To biW - 1 If X > Y Then ARGB = &HFF0000FF '半透明の青 Else ARGB = &HFF '完全透過の青 End If BMPArray(biW * Y + X) = ARGB Next X Next Y 'メモリデバイスコンテキストに編集したビットマップを反映 Ret = SetDIBits(hMemDC, hBitmap, 0, Pic.ScaleHeight, BMPArray(0), BmpInf, 0) 'アルファブレンド Alpha = sld.Value Bld.BlendOp = AC_SRC_OVER Bld.BlendFlags = 0 Bld.SourceConstantAlpha = Alpha '半透明 If chkTrns = 0 Then Bld.AlphaFormat = 0 '透過なし Else Bld.AlphaFormat = AC_SRC_ALPHA '透過あり End If MoveMemory LngBld, Bld, Len(LngBld) Ret = AlphaBlend(Pic.hDC, 0, 0, Pic.ScaleWidth, Pic.ScaleHeight, hMemDC, 0, 0, Pic.ScaleWidth, Pic.ScaleHeight, LngBld) 'メモリ解放 Ret = DeleteDC(hMemDC) Ret = DeleteObject(hBitmap) ReDim BMPArray(0) End Sub '------------------------------------------------------------- '標準モジュール(Module1.bas) '------------------------------------------------------------- Public Type RGBQUAD ' 4 bytes rgbBlue As Byte rgbGreen As Byte rgbRed As Byte rgbReserved As Byte End Type 'BITMAPファイルヘッダー Public Type BITMAPFILEHEADER '14 bytes bfType As Integer bfSize As Long bfReserved1 As Integer bfReserved2 As Integer bfOffBits As Long End Type 'BITMAPインフォヘッダー Public Type BITMAPINFOHEADER '40 bytes biSize As Long biWidth As Long biHeight As Long biPlanes As Integer biBitCount As Integer biCompression As Long biSizeImage As Long biXPelsPerMeter As Long biYPelsPerMeter As Long biClrUsed As Long biClrImportant As Long End Type 'BITMAPインフォ Public Type BITMAPINFO bmiHeader As BITMAPINFOHEADER bmiColors(0 To 255) As RGBQUAD End Type 'ブレンドファンクション構造体 Public Type BlendFunction BlendOp As Byte BlendFlags As Byte SourceConstantAlpha As Byte AlphaFormat As Byte End Type '指定されたデバイスと互換性のあるメモリデバイスコンテキストを作成 Public Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hDC As Long) As Long 'アプリケーションから直接書き込み可能なデバイス独立のビットマップ(DIB)を作成 Public Declare Function CreateDIBSection Lib "gdi32" (ByVal hDC As Long, pBitmapInfo As BITMAPINFO, ByVal un As Long, lplpVoid As Long, ByVal handle As Long, ByVal dw As Long) As Long '指定されたデバイスコンテキストに関連するデバイスと互換性のあるビットマップを作成 Public Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hDC As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long '指定されたデバイスコンテキストに、指定されたオブジェクトを選択 Public Declare Function SelectObject Lib "gdi32" (ByVal hDC As Long, ByVal hObject As Long) As Long '指定されたビットマップのビットを取得し、指定された形式でバッファにコピー Public Declare Function GetDIBits Lib "gdi32" (ByVal aHDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long '指定されたデバイス独立ビットマップ (DIB) の色データを使って、ビットマップにピクセルを設定 Public Declare Function SetDIBits Lib "gdi32" (ByVal hDC As Long, ByVal hBitmap As Long, ByVal nStartScan As Long, ByVal nNumScans As Long, lpBits As Any, lpBI As BITMAPINFO, ByVal wUsage As Long) As Long 'メモリブロックをコピー Declare Sub MoveMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) 'ビットマップをアルファ値に基づいてコピー Public Declare Function AlphaBlend Lib "MSIMG32.dll" (ByVal hdcDest As Long, ByVal nXOriginDest As Long, ByVal nYOriginDest As Long, ByVal nWidthDest As Long, ByVal hHeightDest As Long, ByVal hdcSrc As Long, ByVal nXOriginSrc As Long, ByVal nYOriginSrc As Long, ByVal nWidthSrc As Long, ByVal nHeightSrc As Long, ByVal BlendFunction As Any) As Long 'オブジェクトに関連付けられていたシステムリソースをすべて解放 Public Declare Function DeleteObject Lib "gdi32" (ByVal hObject As Long) As Long 'デバイスコンテキストを削除 Public Declare Function DeleteDC Lib "gdi32" (ByVal hDC As Long) As Long
- 編集済み Iwa55 2014年9月25日 6:54
-
http://msdn.microsoft.com/ja-jp/library/windows/desktop/dd183393(v=vs.85).aspx
Note that the APIs use premultiplied alpha, which means that the red, green and blue channel values in the bitmap must be premultiplied with the alpha channel value. For example, if the alpha channel value is x, the red, green and blue channels must be multiplied by x and divided by 0xff prior to the call.
結局、ここの部分が肝だとおもうのですが。
完全透明の青が000000FF、というのはpremultipliedではありえず、0になるはずです。jzkey
-
jzkeyさま
ご返信ありがとうございます。
おっしゃるとおり、ビットマップのARGB値を&H000000FFから&H00000000にすれば完全透過になりますが、私は、RGB部分の値は保持したまま、Aの部分だけで完全透過をON/OFF制御できないかと考えております。実際のプログラムでは2000*2000ピクセル程度のビットマップを合成表示させようとしており、完全透過のON/OFF制御のたびにRGB部分を毎回計算式で求め直すことを嫌ったからです。仕様であれば仕方がないのですが、解せないのはデスクトップ表示モードをクラシックUIに切り替えた場合に、これが実現できてしまうことです。
syghさまからも質問の意図がいまいちつかめない旨を頂きましたが、質問の意図は、今述べたことになります。
- 編集済み Iwa55 2014年9月25日 10:05
-
だから繰り返し言いますが、GetDIBits()/SetDIBits()を使うのをやめましょう。
第1引数に指定しているのはピクチャーボックスの互換メモリDCですよね? つまりDDBを経由してしまっています。結局肝心な部分は何も変わっていません。私が提示した、MSDNライブラリのサンプルコードでは、GetDIBits()/SetDIBits()は使わず、DIBのBGRAカラーバッファをpvBits経由で直接書き換えています。私の説明をきちんと読んだ上で、さらにサンプルコードを隅々まで読んで意味を理解した後でコードを書くようにしてください。料理と同様で、慣れないうちから自分で勝手にレシピにないアレンジを加えると失敗するのがプログラミングです。
なお旧VBでメモリーブロックへのポインタを経由して配列として扱う方法はWebを探せばいくらでも見つかるので説明はそちらに譲りますが、旧VBよりもVC++、VB.NETやVC#のほうが楽です。旧VBはできるかぎり早く捨てましょう。
Using DIB Sections in VB