Memory is not freed after converting BitmapImage

前端 未结 2 806
春和景丽
春和景丽 2021-02-11 01:05

I got a problem with the following c# (test-)code:

    public static void TestBitmap2ByteArray(BitmapImage bitmap)
    {
        JpegBitmapEncoder encoder = new          


        
2条回答
  •  慢半拍i
    慢半拍i (楼主)
    2021-02-11 01:49

    As described in my other answer a better way to solve this is to access the bitmap data directly. BitmapImage inherits from BitmapSource. BitmapSource is great for this and also works with WPF binding.

    I am using BitmapSource myself to manipulate images that are bound directly to WPF (MVVM style). Basically I create a region in memory and point BitmapSource to it. This allows me to read/write pixels directly to memory and invalidate the BitmapSource so that WPF redraws the image. I have a standard "Bitmap" object I use for this. The direct data access makes it super-fast. (Seriously, no problem at all modifying all bits in 4 images at 30fps .. haven't tried at higher speeds as it hasn't been required.)

    Sample usage can be found on my blog. But basically you do this:

    unsafe {
       byte* imgBytePtr = (byte*)myBitmap.ImageData;
       Int32* imgInt32Ptr = (Int32*)myBitmap.ImageData;
       int height = (int)myBitmap.BitmapSource.Height;
       int width = (int)myBitmap.BitmapSource.Width;
       int bpp = myBitmap.BytesPerPixel;
    
       // Note: No need to iterate just for copy. A Marshal.Copy() at this point can copy all the bytes into a byte-array if you want.
       // But the best would be if your application could do its work directly in the imgBytePtr[]-array.
    
       for (int x = 0; x < height; x++)
       {
          for (int y = 0; y < width; y++)
          {
             // Get bytes into RGBA values
             int bytePos = x * (width * bpp) + (y * bpp);
             byte R = imgBytePtr[bytePos + 0];
             byte B = imgBytePtr[bytePos + 1];
             byte G = imgBytePtr[bytePos + 2];
             byte A = imgBytePtr[bytePos + 3];
    
             // Alternatively get Int32 value of color
             int intPos = x * width + y;
             int intColor = imgIntPtr[intPos];
    
    
             // Examples of manipulating data         
    
             // Remove blue
             imgBytePtr[bytePos + 1] = 0;
             // Alternative remove blue by bitmask
             imgIntPtr[intPos] = imgIntPtr[intPos] & 0xFF00FFFF; 
    
          }
       }
    }
    // Now execute Invalidate() and WPF will automagically update bound picture object :)
    

    This makes a BitmapSource, if you need BitmapImage instead you can see if you can change it to work.

        /// 
        /// This object holds a byte array of the picture as well as a BitmapSource for WPF objects to bind to. Simply call .Invalidate() to update GUI.
        /// 
        public class Bitmap : IDisposable
        {
            // some ideas/code borowed from CL NUI sample CLNUIImage.cs
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern IntPtr CreateFileMapping(IntPtr hFile, IntPtr lpFileMappingAttributes, uint flProtect, uint dwMaximumSizeHigh, uint dwMaximumSizeLow, string lpName);
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap);
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern bool UnmapViewOfFile(IntPtr hMap);
            [DllImport("kernel32.dll", SetLastError = true)]
            static extern bool CloseHandle(IntPtr hHandle);
    
            private IntPtr _section = IntPtr.Zero;
            public IntPtr ImageData { get; private set; }
            public InteropBitmap BitmapSource { get; private set; }
            public int BytesPerPixel = 3;
    
            /// 
            /// Initializes an empty Bitmap
            /// 
            /// Image width
            /// Image height
            /// Image format
            public Bitmap(int width, int height, PixelFormat pixelFormat)
            {
                BytesPerPixel = pixelFormat.BitsPerPixel / 8;
                uint imageSize = (uint)width * (uint)height * (uint)BytesPerPixel;
                // create memory section and map
                _section = CreateFileMapping(new IntPtr(-1), IntPtr.Zero, 0x04, 0, imageSize, null);
                ImageData = MapViewOfFile(_section, 0xF001F, 0, 0, imageSize);
                BitmapSource = Imaging.CreateBitmapSourceFromMemorySection(_section, width, height, pixelFormat, width * BytesPerPixel, 0) as InteropBitmap;
            }
    
            /// 
            /// Invalidates the bitmap causing a redraw
            /// 
            public void Invalidate()
            {
                BitmapSource.Invalidate();
            }
    
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
    
            protected void Dispose(bool disposing)
            {
                if (disposing)
                {
                    // free managed resources
                }
                // free native resources if there are any.
                if (ImageData != IntPtr.Zero)
                {
                    UnmapViewOfFile(ImageData);
                    ImageData = IntPtr.Zero;
                }
                if (_section != IntPtr.Zero)
                {
                    CloseHandle(_section);
                    _section = IntPtr.Zero;
                }
            }
        }
    
    

提交回复
热议问题