Странное поведение с DrawIconEx и GetDIBits, с альфа как ноль - PullRequest
1 голос
/ 03 ноября 2019

Я использую этот код для захвата экрана + курсор:

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);

По какой-то причине, когда курсор - это I-луч (текстовый курсор), область изображения, где он должен бытьнаходится все прозрачно. Информация RGB есть, но альфа-бит 0. Wrong right
(Правильное изображение - это то, как будет выглядеть рамка после того, как все пиксельные альфа-биты установлены на 255 вручную).

Если курсор - стрелка, он работает нормально.

Но если я получу Bitmap из ручки, а затем сохраню на диск, у изображения не будет прозрачного отверстия, как и ожидалось.

Что происходит? Это что-то с DrawIconEx или с FromHbitmap?

Может быть, FromHbitmap всегда устанавливает альфа на 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;

1 Ответ

1 голос
/ 08 ноября 2019

Ну, я подозреваю, что это особый случай. Поскольку я проверил много информации и не нашел подобных случаев.

Но есть два хороших способа справиться с этой ситуацией.

  • Избегайте альфа-канала, установив biBitCount = 24
  • Скопируйте данные курсора вДанные растрового изображения DIBSection с использованием байта альфа-канала для наложения (альфа = 255).

    Создайте контекст устройства и выберите в нем DIBSection.

    Используйте BitBlt() для копирования с нового устройстваконтекст к контексту устройства рисования.

    См .: Как рисовать 32-битные растровые изображения альфа-канала?

...