Используйте собственный HBitmap в C #, сохраняя альфа-канал / прозрачность - PullRequest
3 голосов
/ 07 января 2011

Допустим, я получил объект / дескриптор HBITMAP из встроенной функции Windows. Я могу преобразовать его в управляемое растровое изображение, используя Bitmap.FromHbitmap (nativeHBitmap) , но если собственное изображение содержит информацию о прозрачности (альфа-канал), оно теряется при таком преобразовании.

Есть несколько вопросов по переполнению стека относительно этой проблемы. Используя информацию из первого ответа на этот вопрос ( Как нарисовать растровое изображение ARGB с использованием GDI +? ), я написал фрагмент кода, который я пробовал, и он работает.

Он в основном получает собственную ширину HBitmap, высоту и указатель на местоположение данных пикселя, используя GetObject и структуру BITMAP , а затем вызывает конструктор управляемого растрового изображения:

Bitmap managedBitmap = new Bitmap(bitmapStruct.bmWidth, bitmapStruct.bmHeight,
    bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);

Как я понимаю (пожалуйста, исправьте меня, если я ошибаюсь), это не копирует фактические данные пикселей из собственного HBitmap в управляемое растровое изображение, оно просто указывает управляемое растровое изображение на данные пикселей из собственного HBitmap.

И я не рисую растровое изображение здесь на другой графике (DC) или на другом растровом изображении, чтобы избежать ненужного копирования памяти, особенно для больших растровых изображений.

Я могу просто назначить это растровое изображение элементу управления PictureBox или свойству Form BackgroundImage. И это работает, растровое изображение отображается правильно, с использованием прозрачности.

Когда я больше не использую растровое изображение, я проверяю, что свойство BackgroundImage больше не указывает на растровое изображение, и располагаю как управляемым растровым изображением, так и собственным HBitmap.

Вопрос: Можете ли вы сказать мне, если это рассуждение и код кажутся правильными. Я надеюсь, что я не получу неожиданное поведение или ошибки. И я надеюсь, что освобождаю всю память и объекты правильно.

    private void Example()
    {
        IntPtr nativeHBitmap = IntPtr.Zero;

        /* Get the native HBitmap object from a Windows function here */

        // Create the BITMAP structure and get info from our nativeHBitmap
        NativeMethods.BITMAP bitmapStruct = new NativeMethods.BITMAP();
        NativeMethods.GetObjectBitmap(nativeHBitmap, Marshal.SizeOf(bitmapStruct), ref bitmapStruct);

        // Create the managed bitmap using the pointer to the pixel data of the native HBitmap
        Bitmap managedBitmap = new Bitmap(
            bitmapStruct.bmWidth, bitmapStruct.bmHeight, bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);

        // Show the bitmap
        this.BackgroundImage = managedBitmap;

        /* Run the program, use the image */
        MessageBox.Show("running...");

        // When the image is no longer needed, dispose both the managed Bitmap object and the native HBitmap
        this.BackgroundImage = null;
        managedBitmap.Dispose();
        NativeMethods.DeleteObject(nativeHBitmap);
    }

internal static class NativeMethods
{
    [StructLayout(LayoutKind.Sequential)]
    public struct BITMAP
    {
        public int bmType;
        public int bmWidth;
        public int bmHeight;
        public int bmWidthBytes;
        public ushort bmPlanes;
        public ushort bmBitsPixel;
        public IntPtr bmBits;
    }

    [DllImport("gdi32", CharSet = CharSet.Auto, EntryPoint = "GetObject")]
    public static extern int GetObjectBitmap(IntPtr hObject, int nCount, ref BITMAP lpObject);

    [DllImport("gdi32.dll")]
    internal static extern bool DeleteObject(IntPtr hObject);
}

Ответы [ 3 ]

8 голосов
/ 15 февраля 2012

Следующий код работал для меня, даже если HBITMAP является значком или BMP, он не переворачивает изображение, когда это значок, а также работает с растровыми изображениями, которые не содержат альфа-канал:1003 *

2 голосов
/ 07 января 2011

Правильно, копия не сделана. Вот почему в разделе «Примечания» библиотеки MSDN говорится:

Звонящий отвечает за выделение и освобождение блока память, указанная в scan0 параметр, однако, память должна не будет выпущен, пока связанный Растровое изображение выпущено.

Это не будет проблемой, если данные пикселей будут скопированы. Между прочим, обычно это сложная проблема. Вы не можете сказать, когда клиентский код называется Dispose (), нет способа перехватить этот вызов. Что делает невозможным заставить такое растровое изображение вести себя как замена растрового изображения. Код клиента должен знать, что требуется дополнительная работа.

1 голос
/ 07 января 2011

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

Я создаю два управляемых растровых объекта (но только один выделяет память для фактических данных пикселей) и использую graphics.DrawImage для копирования изображения.Есть ли лучший способ сделать это?Или это хорошо / достаточно быстро?

    public static Bitmap CopyHBitmapToBitmap(IntPtr nativeHBitmap)
    {
        // Get width, height and the address of the pixel data for the native HBitmap
        NativeMethods.BITMAP bitmapStruct = new NativeMethods.BITMAP();
        NativeMethods.GetObjectBitmap(nativeHBitmap, Marshal.SizeOf(bitmapStruct), ref bitmapStruct);

        // Create a managed bitmap that has its pixel data pointing to the pixel data of the native HBitmap
        // No memory is allocated for its pixel data
        Bitmap managedBitmapPointer = new Bitmap(
            bitmapStruct.bmWidth, bitmapStruct.bmHeight, bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);

        // Create a managed bitmap and allocate memory for pixel data
        Bitmap managedBitmapReal = new Bitmap(bitmapStruct.bmWidth, bitmapStruct.bmHeight, PixelFormat.Format32bppArgb);

        // Copy the pixels of the native HBitmap into the canvas of the managed bitmap
        Graphics graphics = Graphics.FromImage(managedBitmapReal);
        graphics.DrawImage(managedBitmapPointer, 0, 0);

        // Delete the native HBitmap object and free memory
        NativeMethods.DeleteObject(nativeHBitmap);

        // Return the managed bitmap, clone of the native HBitmap, with correct transparency
        return managedBitmapReal;
    }
...