#if NET452 using System; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Threading.Tasks; namespace Unosquare.Swan.Formatters { /// /// Represents a buffer of bytes containing pixels in BGRA byte order /// loaded from an image that is passed on to the constructor. /// Data contains all the raw bytes (without scanline left-over bytes) /// where they can be quickly changed and then a new bitmap /// can be created from the byte data. /// public class BitmapBuffer { /// /// A constant representing the number of /// bytes per pixel in the pixel data. This is /// always 4 but it is kept here for readability. /// public const Int32 BytesPerPixel = 4; /// /// The blue byte offset within a pixel offset. This is 0. /// public const Int32 BOffset = 0; /// /// The green byte offset within a pixel offset. This is 1. /// public const Int32 GOffset = 1; /// /// The red byte offset within a pixel offset. This is 2. /// public const Int32 ROffset = 2; /// /// The alpha byte offset within a pixel offset. This is 3. /// public const Int32 AOffset = 3; /// /// Initializes a new instance of the class. /// Data will not contain left-over stride bytes /// /// The source image. public BitmapBuffer(Image sourceImage) { // Acquire or create the source bitmap in a manageable format Boolean disposeSourceBitmap = false; if(!(sourceImage is Bitmap sourceBitmap) || sourceBitmap.PixelFormat != this.PixelFormat) { sourceBitmap = new Bitmap(sourceImage.Width, sourceImage.Height, this.PixelFormat); using(Graphics g = Graphics.FromImage(sourceBitmap)) { g.DrawImage(sourceImage, 0, 0); } // We created this bitmap. Make sure we clear it from memory disposeSourceBitmap = true; } // Lock the bits BitmapData sourceDataLocker = sourceBitmap.LockBits( new Rectangle(0, 0, sourceBitmap.Width, sourceBitmap.Height), ImageLockMode.ReadOnly, sourceBitmap.PixelFormat); // Set basic properties this.ImageWidth = sourceBitmap.Width; this.ImageHeight = sourceBitmap.Height; this.LineStride = sourceDataLocker.Stride; // State variables this.LineLength = sourceBitmap.Width * BytesPerPixel; // may or may not be equal to the Stride this.Data = new Byte[this.LineLength * sourceBitmap.Height]; // copy line by line in order to ignore the useless left-over stride _ = Parallel.For(0, sourceBitmap.Height, y => { IntPtr sourceAddress = sourceDataLocker.Scan0 + sourceDataLocker.Stride * y; Int32 targetAddress = y * this.LineLength; Marshal.Copy(sourceAddress, this.Data, targetAddress, this.LineLength); }); // finally unlock the bitmap sourceBitmap.UnlockBits(sourceDataLocker); // dispose the source bitmap if we had to create it if(disposeSourceBitmap) { sourceBitmap.Dispose(); } } /// /// Contains all the bytes of the pixel data /// Each horizontal scanline is represented by LineLength /// rather than by LinceStride. The left-over stride bytes /// are removed. /// public Byte[] Data { get; } /// /// Gets the width of the image. /// public Int32 ImageWidth { get; } /// /// Gets the height of the image. /// public Int32 ImageHeight { get; } /// /// Gets the pixel format. This will always be Format32bppArgb. /// public PixelFormat PixelFormat { get; } = PixelFormat.Format32bppArgb; /// /// Gets the length in bytes of a line of pixel data. /// Basically the same as Line Length except Stride might be a little larger as /// some bitmaps might be DWORD-algned. /// public Int32 LineStride { get; } /// /// Gets the length in bytes of a line of pixel data. /// Basically the same as Stride except Stride might be a little larger as /// some bitmaps might be DWORD-algned. /// public Int32 LineLength { get; } /// /// Gets the index of the first byte in the BGRA pixel data for the given image coordinates. /// /// The x. /// The y. /// Index of the first byte in the BGRA pixel. /// /// x /// or /// y. /// public Int32 GetPixelOffset(Int32 x, Int32 y) { if(x < 0 || x > this.ImageWidth) { throw new ArgumentOutOfRangeException(nameof(x)); } if(y < 0 || y > this.ImageHeight) { throw new ArgumentOutOfRangeException(nameof(y)); } return y * this.LineLength + x * BytesPerPixel; } /// /// Converts the pixel data bytes held in the buffer /// to a 32-bit RGBA bitmap. /// /// Pixel data for a graphics image and its attribute. public Bitmap ToBitmap() { Bitmap bitmap = new Bitmap(this.ImageWidth, this.ImageHeight, this.PixelFormat); BitmapData bitLocker = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, bitmap.PixelFormat); _ = Parallel.For(0, bitmap.Height, y => { Int32 sourceOffset = this.GetPixelOffset(0, y); IntPtr targetOffset = bitLocker.Scan0 + y * bitLocker.Stride; Marshal.Copy(this.Data, sourceOffset, targetOffset, bitLocker.Width); }); bitmap.UnlockBits(bitLocker); return bitmap; } } } #endif