استخدم hbitmap الأصلي في C# مع الحفاظ على قناة/شفافية ألفا

StackOverflow https://stackoverflow.com/questions/4627376

سؤال

دعنا نقول أنني أحصل على كائن/مقبض HBITMAP من دالة Windows الأصلية. يمكنني تحويله إلى صورة نقطية مُدارة باستخدام bitmap.fromhbitmap (nativeHbitMap), ، ولكن إذا كانت الصورة الأصلية تحتوي على معلومات شفافية (قناة ألفا) ، فسيضيعها هذا التحويل.

هناك بعض الأسئلة حول سعة مكدس فيما يتعلق بهذه القضية. استخدام معلومات من الإجابة الأولى من هذا السؤال (كيفية رسم صورة نقطية ArgB باستخدام GDI+؟) ، كتبت قطعة من الكود جربتها وهي تعمل.

إنه يحصل بشكل أساسي على عرض HBITMAP الأصلي ، والارتفاع والمؤشر إلى موقع بيانات البكسل باستخدام getObject و ال صورة نقطية الهيكل ، ثم يدعو مُنشئ نقطية مُدارة:

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);
}
هل كانت مفيدة؟

المحلول

صحيح ، لم يتم إجراء نسخة. وهذا هو السبب في أن قسم الملاحظات في مكتبة MSDN يقول:

يكون المتصل مسؤولاً عن تخصيص وتخفيف كتلة الذاكرة المحددة بواسطة معلمة SCAN0 ، ومع ذلك ، يجب عدم إصدار الذاكرة حتى يتم إصدار صورة نقطية ذات صلة.

لن تكون هذه مشكلة إذا تم نسخ بيانات البكسل. بالمناسبة ، هذه عادة ما تكون مشكلة صعبة للتعامل معها. لا يمكنك معرفة متى يسمى رمز العميل Dispose () ، لا توجد طريقة لاعتراض هذه المكالمة. مما يجعل من المستحيل جعل مثل هذا النقطات يتصرف مثل بديل لنقطة الصورة النقطية. يجب أن يكون رمز العميل على دراية بأن هناك حاجة إلى عمل إضافي.

نصائح أخرى

عملت الرمز التالي بالنسبة لي حتى لو HBITMAP هو أيقونة أو BMP ، فهو لا يقلب الصورة عندما يكون رمزًا ، ويعمل أيضًا مع نقار نقطي لا يحتوي على قناة ألفا:

    private static Bitmap GetBitmapFromHBitmap(IntPtr nativeHBitmap)
    {
        Bitmap bmp = Bitmap.FromHbitmap(nativeHBitmap);

        if (Bitmap.GetPixelFormatSize(bmp.PixelFormat) < 32)
            return bmp;

        BitmapData bmpData;

        if (IsAlphaBitmap(bmp, out bmpData))
            return GetlAlphaBitmapFromBitmapData(bmpData);

        return bmp;
    }

    private static Bitmap GetlAlphaBitmapFromBitmapData(BitmapData bmpData)
    {
        return new Bitmap(
                bmpData.Width,
                bmpData.Height,
                bmpData.Stride,
                PixelFormat.Format32bppArgb,
                bmpData.Scan0);
    }

    private static bool IsAlphaBitmap(Bitmap bmp, out BitmapData bmpData)
    {
        Rectangle bmBounds = new Rectangle(0, 0, bmp.Width, bmp.Height);

        bmpData = bmp.LockBits(bmBounds, ImageLockMode.ReadOnly, bmp.PixelFormat);

        try
        {
            for (int y = 0; y <= bmpData.Height - 1; y++)
            {
                for (int x = 0; x <= bmpData.Width - 1; x++)
                {
                    Color pixelColor = Color.FromArgb(
                        Marshal.ReadInt32(bmpData.Scan0, (bmpData.Stride * y) + (4 * x)));

                    if (pixelColor.A > 0 & pixelColor.A < 255)
                    {
                        return true;
                    }
                }
            }
        }
        finally
        {
            bmp.UnlockBits(bmpData);
        }

        return false;
    }

بعد قراءة النقاط الجيدة التي أدلى بها Hans Passant في إجابته ، غيرت الطريقة لنسخ بيانات البكسل على الفور إلى صورة نقطية مُدارة ، وحرر الصورة النقطية الأصلية.

أقوم بإنشاء كائنين من نوع النقطات المدارة (ولكن واحد فقط يخصص الذاكرة لبيانات البكسل الفعلية) ، والاستخدام 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;
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top