7 months ago, we began to study C # and WPF, and, like all newbies who want to do some image processing, we came across this question:
Why is there a bitmap and a BitmapSource? And what are the advantages of each of them?
In our project, we needed to create a bitmap image from the data. Speed was very important to us.
We started with Bitmap because it is simpler (expecialy: get / setpixel methods) well documented with lots of examples. But then we discovered WPF conversion problems for printing a bitmap.
So, we tried with BitmapSource, it was not easy due to different pixels. But we have finally succeeded.
We compared the speed of each generation. Working with SetPixel (Bitmap) is much slower than working with a byte array (BitmapSource), but working with an array of bytes means complications: step, pixel format ...
So, we have chosen BitmapSource. But then we wanted to serialize some BitmapSource. BitmapSource is not serializable. Thus, using [OnSerializing] [OnDeserialized] we converted BitmapSource to Bitmap (serializable).
Our findings:
Bitmap Benefits:
- Simplicity
- Serializability
Benefits of BitmapSource:
- Generation rate
- Inheritance (ImageSource) for WPF
Do you see some other points?
To illustrate for beginners like us, here are some useful methods we need:
Conversion:
public static System.Windows.Media.Imaging.BitmapSource BitmapToBitmapSource(System.Drawing.Bitmap source) { using (MemoryStream memory = new MemoryStream()) { source.Save(memory, ImageFormat.Png); memory.Position = 0; BitmapImage bitmapImage = new BitmapImage(); bitmapImage.BeginInit(); bitmapImage.StreamSource = memory; bitmapImage.CacheOption = BitmapCacheOption.OnLoad; bitmapImage.EndInit(); return bitmapImage; } } public static System.Drawing.Bitmap BitmapFromSource(BitmapSource source) { using (MemoryStream outStream = new MemoryStream()) { BitmapEncoder enc = new PngBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(source)); enc.Save(outStream); System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
Opening an image without blocking:
public static BitmapImage LoadImage(string uri) { BitmapImage monImage = null; if (uri != null) { BitmapImage image = new BitmapImage(); using (FileStream stream = File.OpenRead(uri)) { image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = stream; image.EndInit(); } monImage = image; } return monImage; }
Resize BitmapSource:
public static BitmapImage BitmapImageFromBitmapSourceResized(BitmapSource bitmapSource, int newWidth) { BmpBitmapEncoder encoder = new BmpBitmapEncoder(); MemoryStream memoryStream = new MemoryStream(); BitmapImage bImg = new BitmapImage(); encoder.Frames.Add(BitmapFrame.Create(bitmapSource)); encoder.Save(memoryStream); bImg.BeginInit(); bImg.StreamSource = new MemoryStream(memoryStream.ToArray()); bImg.DecodePixelWidth = newWidth; bImg.EndInit(); memoryStream.Close(); return bImg; }
Generation:
public static int GetBytesPerPixel(BitmapSource bmp) { return (bmp.Format.BitsPerPixel + 7) / 8; } public static int GetStrideFromeBitmapSource(BitmapSource bmp) { return 4 * ((bmp.PixelWidth * GetBytesPerPixel(bmp) + 3) / 4); } public static byte[] GetBytesFromBitmapSource(BitmapSource bmp) { int height = bmp.PixelHeight; int stride = GetStrideFromeBitmapSource(bmp); byte[] pixels = new byte[height * stride]; bmp.CopyPixels(pixels, stride, 0); return pixels; } public static int GetWidth(int stride, int bytesPerPixel) { int width = (int)( (float)stride / (float)bytesPerPixel ); return width; } public static int GetHeight(byte[] bits, int stride) { int height = (int)( (float)bits.Length / (float)stride ); return height; } public static void SetPixelRgb24(ref byte[] bits, int x, int y, int stride, Color c) { bits[x * 3 + y * stride] = cR; bits[x * 3 + y * stride + 1] = cG; bits[x * 3 + y * stride + 2] = cB; } public static void SetPixelBgra32(ref byte[] bits, int x, int y, int stride, Couleur c) { bits[x * 4 + y * stride + 0] = cB; bits[x * 4 + y * stride + 1] = cG; bits[x * 4 + y * stride + 2] = cR; bits[x * 4 + y * stride + 3] = cA; } public static int GetAverageValueOfPixel(ref byte[] bits, int x, int y, int stride, int bytesPerPixel) { int sum = 0; for (var i = 0; i < bytesPerPixel; i++) sum += bits[x * bytesPerPixel + y * stride + i]; return (int) ( sum * (255f / (255f * bytesPerPixel)) ); }
Snapshot from BitmapSource:
public static BitmapSource SnapShotToBitmap(this UIElement source, double zoomX, double zoomY) { try { DataObject dataObject = new DataObject(); double actualHeight = source.RenderSize.Height; double actualWidth = source.RenderSize.Width; if (actualHeight == 0) actualHeight = 1; if (actualWidth == 0) actualWidth = 1; double renderHeight = actualHeight * zoomY; double renderWidth = actualWidth * zoomX; RenderTargetBitmap renderTarget = new RenderTargetBitmap((int)renderWidth, (int)renderHeight, 96, 96, PixelFormats.Pbgra32); VisualBrush sourceBrush = new VisualBrush(source); DrawingVisual drawingVisual = new DrawingVisual(); DrawingContext drawingContext = drawingVisual.RenderOpen(); using (drawingContext) { drawingContext.PushTransform(new ScaleTransform(zoomX, zoomY)); drawingContext.DrawRectangle(sourceBrush, null, new Rect(new Point(0, 0), new Point(actualWidth, actualHeight))); } renderTarget.Render(drawingVisual); return renderTarget; } catch (Exception e) { throw new Exception(e); } }