DrawIconEx 和 GetDIBits 的奇怪行为,alpha 为零
Strange behavior with DrawIconEx and GetDIBits, with alpha as zero
我正在使用此代码捕获屏幕 + 光标:
new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand();
var success = Native.BitBlt(_compatibleDeviceContext, 0, 0, Width, Height, _windowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt);
if (!success)
return FrameCount;
try
{
var cursorInfo = new Native.CursorInfo();
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
if (Native.GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == Native.CursorShowing)
{
var hicon = Native.CopyIcon(cursorInfo.hCursor);
if (hicon != IntPtr.Zero)
{
if (Native.GetIconInfo(hicon, out var iconInfo))
{
frame.CursorX = cursorInfo.ptScreenPos.X - Left;
frame.CursorY = cursorInfo.ptScreenPos.Y - Top;
//if (frame.CursorX > 0 && frame.CursorY > 0)
Native.DrawIconEx(_compatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, 0, IntPtr.Zero, 0x0003);
}
Native.DeleteObject(iconInfo.hbmColor);
Native.DeleteObject(iconInfo.hbmMask);
}
Native.DestroyIcon(hicon);
}
Native.DeleteObject(cursorInfo.hCursor);
}
}
catch (Exception)
{
//LogWriter.Log(e, "Impossible to get the cursor");
}
frame.DataLength = _byteLength;
frame.Data = new byte[_byteLength];
//If saving to disk as bitmap, it works.
//System.Drawing.Image.FromHbitmap(_compatibleBitmap).Save(frame.Path);
//If getting the image as pixel array, the square where the cursor is located is all transparent.
Native.GetDIBits(_windowDeviceContext, _compatibleBitmap, 0, (uint)Height, frame.Data, ref _infoHeader, Native.DibColorMode.DibRgbColors);
由于某些原因,当光标是工字梁(文本光标)时,它应该位于的图像区域是全透明的。
RGB 信息在那里,但 alpha 位是 0
。
(右图是所有像素 alpha 位手动设置为 255 后帧的样子)。
如果光标是箭头,则正常。
但是如果我从句柄中获取 Bitmap
然后保存到磁盘,图像没有透明孔,正如预期的那样。
这是怎么回事?
是 DrawIconEx
还是 FromHbitmap
?
也许 FromHbitmap
总是将所有像素的 alpha 设置为 255?
也许 GetDiBits
以不同方式合并两个图像(屏幕截图 + 光标)?
编辑
作为快速修复,我知道我可以检测光标是否为 I-Beam(蒙版单色类型)并在保存像素阵列时手动修复它。
//Set to fix all alpha bits back to 255.
frame.RemoveAnyTransparency = iconInfo.hbmMask != IntPtr.Zero;
并且:
if (frame.RemoveAnyTransparency)
for (var i = 3; i < _byteLength; i += 4)
frame.Data[i] = 255;
嗯,我怀疑这是一个特例。因为查了很多资料都没有找到类似的案例。
但是有两种很好的方法来处理这种情况。
- 通过设置
biBitCount = 24
避免 alpha 通道
将游标数据复制到 DIBSection 的位图数据中,使用
要混合的 alpha 通道字节(alpha = 255)。
创建一个设备上下文并将 select DIBSection 放入其中。
使用BitBlt()
从新设备上下文复制到绘制设备上下文。
参考:How to draw 32-bit alpha channel bitmaps?
我正在使用此代码捕获屏幕 + 光标:
new System.Security.Permissions.UIPermission(System.Security.Permissions.UIPermissionWindow.AllWindows).Demand();
var success = Native.BitBlt(_compatibleDeviceContext, 0, 0, Width, Height, _windowDeviceContext, Left, Top, Native.CopyPixelOperation.SourceCopy | Native.CopyPixelOperation.CaptureBlt);
if (!success)
return FrameCount;
try
{
var cursorInfo = new Native.CursorInfo();
cursorInfo.cbSize = Marshal.SizeOf(cursorInfo);
if (Native.GetCursorInfo(out cursorInfo))
{
if (cursorInfo.flags == Native.CursorShowing)
{
var hicon = Native.CopyIcon(cursorInfo.hCursor);
if (hicon != IntPtr.Zero)
{
if (Native.GetIconInfo(hicon, out var iconInfo))
{
frame.CursorX = cursorInfo.ptScreenPos.X - Left;
frame.CursorY = cursorInfo.ptScreenPos.Y - Top;
//if (frame.CursorX > 0 && frame.CursorY > 0)
Native.DrawIconEx(_compatibleDeviceContext, frame.CursorX - iconInfo.xHotspot, frame.CursorY - iconInfo.yHotspot, cursorInfo.hCursor, 0, 0, 0, IntPtr.Zero, 0x0003);
}
Native.DeleteObject(iconInfo.hbmColor);
Native.DeleteObject(iconInfo.hbmMask);
}
Native.DestroyIcon(hicon);
}
Native.DeleteObject(cursorInfo.hCursor);
}
}
catch (Exception)
{
//LogWriter.Log(e, "Impossible to get the cursor");
}
frame.DataLength = _byteLength;
frame.Data = new byte[_byteLength];
//If saving to disk as bitmap, it works.
//System.Drawing.Image.FromHbitmap(_compatibleBitmap).Save(frame.Path);
//If getting the image as pixel array, the square where the cursor is located is all transparent.
Native.GetDIBits(_windowDeviceContext, _compatibleBitmap, 0, (uint)Height, frame.Data, ref _infoHeader, Native.DibColorMode.DibRgbColors);
由于某些原因,当光标是工字梁(文本光标)时,它应该位于的图像区域是全透明的。
RGB 信息在那里,但 alpha 位是 0
。
(右图是所有像素 alpha 位手动设置为 255 后帧的样子)。
如果光标是箭头,则正常。
但是如果我从句柄中获取 Bitmap
然后保存到磁盘,图像没有透明孔,正如预期的那样。
这是怎么回事?
是 DrawIconEx
还是 FromHbitmap
?
也许 FromHbitmap
总是将所有像素的 alpha 设置为 255?
也许 GetDiBits
以不同方式合并两个图像(屏幕截图 + 光标)?
编辑
作为快速修复,我知道我可以检测光标是否为 I-Beam(蒙版单色类型)并在保存像素阵列时手动修复它。
//Set to fix all alpha bits back to 255.
frame.RemoveAnyTransparency = iconInfo.hbmMask != IntPtr.Zero;
并且:
if (frame.RemoveAnyTransparency)
for (var i = 3; i < _byteLength; i += 4)
frame.Data[i] = 255;
嗯,我怀疑这是一个特例。因为查了很多资料都没有找到类似的案例。
但是有两种很好的方法来处理这种情况。
- 通过设置
biBitCount = 24
避免 alpha 通道
将游标数据复制到 DIBSection 的位图数据中,使用 要混合的 alpha 通道字节(alpha = 255)。
创建一个设备上下文并将 select DIBSection 放入其中。
使用
BitBlt()
从新设备上下文复制到绘制设备上下文。参考:How to draw 32-bit alpha channel bitmaps?