poniedziałek, 24 grudnia 2012

Bitmapowy zawrót głowy

Dziś bez zbędnych wprowadzeń i opisów - po prostu fragmenty kodu odpowiedzialne za konwersję pomiędzy trzema klasami odpowiedzialnymi za grafikę w .NET: Bitmap, BitmapImage oraz BitmapSource. Natomiast w ramach bonusu świątecznego konwersja z i do Byte[], która z pewnością w przypadku trzymania obrazów w plikach/bazach danych przyda się "od kopa". Przedstawiony kod jest w postaci metod rozszerzających (ang. extension methods), więc umożliwi przejrzyste i proste wykorzystanie.

Bitmap

using System;
using System.Drawing;
using System.Windows.Media.Imaging;
using System.IO;
using System.Drawing.Imaging;
using System.Windows.Interop;

namespace Runaurufu.Extension
{
    public static class ExtensionBitmap
    {
        public static BitmapImage ToBitmapImage(this Bitmap A)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                A.Save(ms, ImageFormat.Bmp);
                ms.Position = 0;
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
                return bi;
            }
        }

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        private static extern bool DeleteObject(IntPtr hObject);

        public static BitmapSource ToBitmapSource(this Bitmap A)
        {
            IntPtr hBitmap = A.GetHbitmap();
            BitmapSource result;
            try
            {
                result = Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, System.Windows.Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
            }
            finally
            {
                DeleteObject(hBitmap);
            }

            return result;
        }

        public static Byte[] ToByteArray(this Bitmap A)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                A.Save(ms, ImageFormat.Bmp);
                ms.Position = 0;
                return ms.ToArray();
            }
        }
    }
}

BitmapSource

using System;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;

namespace Runaurufu.Extension
{
    public static class ExtensionBitmapSource
    {
        public static BitmapImage ToBitmapImage(this BitmapSource A)
        {
            return A.ToBitmapImage(new BmpBitmapEncoder());
        }

        public static BitmapImage ToBitmapImage(this BitmapSource A, BitmapEncoder encoder)
        {
            BitmapImage bi = new BitmapImage();

            using (MemoryStream ms = new MemoryStream())
            {
                encoder.Frames.Add(BitmapFrame.Create(A));
                encoder.Save(ms);
                ms.Position = 0;

                bi.BeginInit();
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
            }

            return bi;
        }

        public static Bitmap ToBitmap(this BitmapSource A)
        {
            return A.ToBitmap(new BmpBitmapEncoder());
        }

        public static Bitmap ToBitmap(this BitmapSource A, BitmapEncoder encoder)
        {
            System.Drawing.Bitmap bitmap;
            using (MemoryStream outStream = new MemoryStream())
            {
                encoder.Frames.Add(BitmapFrame.Create(A));
                encoder.Save(outStream);
                bitmap = new System.Drawing.Bitmap(outStream);
            }
            return bitmap;
        }

        public static Byte[] ToByteArray(this BitmapSource A)
        {
            return A.ToByteArray(new BmpBitmapEncoder());
        }

        public static Byte[] ToByteArray(this BitmapSource A, BitmapEncoder encoder)
        {
            using (MemoryStream memoryStream = new MemoryStream())
            {
                encoder.Frames.Add(BitmapFrame.Create(A));
                encoder.Save(memoryStream);
                return memoryStream.GetBuffer();
            }
        }
    }
}

BitmapImage

using System;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;

namespace Runaurufu.Extension
{
    public static class ExtensionBitmapImage
    {
        public static Bitmap ToBitmap(this BitmapImage A)
        {
            return A.ToBitmap(new BmpBitmapEncoder());
        }

        public static Bitmap ToBitmap(this BitmapImage A, BitmapEncoder encoder)
        {
            Bitmap bitmap;
            using (MemoryStream outStream = new MemoryStream())
            {
                encoder.Frames.Add(BitmapFrame.Create(A));
                encoder.Save(outStream);
                bitmap = new System.Drawing.Bitmap(outStream);
            }
            return bitmap;
        }

        public static Byte[] ToByteArray(this BitmapImage A)
        {
            return A.ToByteArray(new BmpBitmapEncoder());
        }

        public static Byte[] ToByteArray(this BitmapImage A, BitmapEncoder encoder)
        {
            Byte[] byteArr;
            using (MemoryStream ms = new MemoryStream())
            {
                encoder.Frames.Add(BitmapFrame.Create(A));
                encoder.Save(ms);
                ms.Position = 0;
                byteArr = ms.GetBuffer();
            }
            return byteArr;
        }
    }
}

Byte[]

using System;
using System.Drawing;
using System.IO;
using System.Windows.Media.Imaging;

namespace Runaurufu.Extension
{
    public static class ExtensionByte
    {
        public static Bitmap ToBitmap(this Byte[] A)
        {
            using (MemoryStream ms = new MemoryStream(A))
            {
                return new Bitmap(ms);
            }
        }

        public static BitmapImage ToBitmapImage(this Byte[] A)
        {
            using (MemoryStream ms = new MemoryStream(A))
            {
                ms.Position = 0;
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
                return bi;
            }
        }
    }
}

Warto zwrócić uwagę, że w przypadku BitmapSource czyli obrazach rodem z WPF/Silverlighta konieczne jest podanie odpowiedniego enkodera - wybór np. JpegEncoder na obrazie z kanałem alfa (przeźroczystość) sprawi, że ów kanał alfa trafi szlag... :) Dodatkowo z oczywistych przyczyn nie ma metody do konwersji BitmapImage -> BitmapSource - a jeśli ktoś szuka bo z jakiś powodów potrzebuje to może wykorzystać ten fragment:
        public static BitmapSource ToBitmapSource (this BitmapImage A)
        {
            return (BitmapSource)A;
        }

A wszystkim, którzy choć od czasu do czasu zaglądają w me skromne progi życzę Wesołych Świąt :)

Keywords: C#, . NET, Bitmap, BitmapImage, BitmapSource, Bitmap conversions, Silverlight, WPF, WinForm, Image, Picture, Transparent BitmapImage.