Use native HBitmap in C# while preserving alpha channel/transparency - c#

Let's say I get a HBITMAP object/handle from a native Windows function. I can convert it to a managed bitmap using Bitmap.FromHbitmap(nativeHBitmap), but if the native image has transparency information (alpha channel), it is lost by this conversion.
There are a few questions on Stack Overflow regarding this issue. Using information from the first answer of this question (How to draw ARGB bitmap using GDI+?), I wrote a piece of code that I've tried and it works.
It basically gets the native HBitmap width, height and the pointer to the location of the pixel data using GetObject and the BITMAP structure, and then calls the managed Bitmap constructor:
Bitmap managedBitmap = new Bitmap(bitmapStruct.bmWidth, bitmapStruct.bmHeight,
bitmapStruct.bmWidth * 4, PixelFormat.Format32bppArgb, bitmapStruct.bmBits);
As I understand (please correct me if I'm wrong), this does not copy the actual pixel data from the native HBitmap to the managed bitmap, it simply points the managed bitmap to the pixel data from the native HBitmap.
And I don't draw the bitmap here on another Graphics (DC) or on another bitmap, to avoid unnecessary memory copying, especially for large bitmaps.
I can simply assign this bitmap to a PictureBox control or the the Form BackgroundImage property. And it works, the bitmap is displayed correctly, using transparency.
When I no longer use the bitmap, I make sure the BackgroundImage property is no longer pointing to the bitmap, and I dispose both the managed bitmap and the native HBitmap.
The Question: Can you tell me if this reasoning and code seems correct. I hope I will not get some unexpected behaviors or errors. And I hope I'm freeing all the memory and objects correctly.
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);
}

The following code worked for me even if the HBITMAP is an icon or bmp, it doesn't flip the image when it's an icon, and also works with bitmaps that don't contain Alpha channel:
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;
}

Right, no copy is made. Which is why the Remarks section of the MSDN Library says:
The caller is responsible for
allocating and freeing the block of
memory specified by the scan0
parameter, however, the memory should
not be released until the related
Bitmap is released.
This wouldn't be a problem if the pixel data was copied. Incidentally, this is normally a difficult problem to deal with. You can't tell when the client code called Dispose(), there's no way to intercept that call. Which makes it impossible to make such a bitmap behave like a replacement for Bitmap. The client code has to be aware that additional work is needed.

After reading the good points made by Hans Passant in his answer, I changed the method to immediately copy the pixel data into the managed bitmap, and free the native bitmap.
I'm creating two managed bitmap objects (but only one allocates memory for the actual pixel data), and use graphics.DrawImage to copy the image. Is there a better way to accomplish this? Or is this good/fast enough?
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;
}

Related

Converting 24bpp to 8bpp - How can I fix this method so it returns a Bitmap with positive stride

I'm looking for a fast way to convert a Bitmap from 24bpp to 8bpp.
I found a solution at this site Programmer » 1bpp in C#. It works, but the resulting Bitmap stride is negative!
How can I fix the stride of the image?
I've already tried this:
Bitmap.Clone: stride still negative
new Bitmap(b0): Creates a 32bpp image... ¬¬
EDIT:
Save to a file, read it back and make a deep copy (Marshal.Copy or memcopy) of pixel data to detach the Bitmap from the file works.. But is "kind of" ugly...
The code is below:
/// <summary>
/// Copy a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast
/// </summary>
/// <param name="source">original bitmap</param>
/// <param name="bpp">1 or 8, target bpp</param>
/// <returns>a 1bpp copy of the bitmap</returns>
/// <url>http://www.wischik.com/lu/programmer/1bpp.html</url>
private static Bitmap ConvertTo1or8bppNegativeStride(Bitmap source, int bpp = 8)
{
if (bpp != 1 && bpp != 8) throw new System.ArgumentException("1 or 8", "bpp");
// Plan: built into Windows GDI is the ability to convert
// bitmaps from one format to another. Most of the time, this
// job is actually done by the graphics hardware accelerator card
// and so is extremely fast. The rest of the time, the job is done by
// very fast native code.
// We will call into this GDI functionality from C#. Our plan:
// (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
// (2) Create a GDI monochrome hbitmap
// (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
// (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)
int w = source.Width, h = source.Height;
IntPtr hbm = source.GetHbitmap(); // this is step (1)
//
// Step (2): create the monochrome bitmap.
// "BITMAPINFO" is an interop-struct which we define below.
// In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
BITMAPINFO bmi = new BITMAPINFO();
bmi.biSize = 40; // the size of the BITMAPHEADERINFO struct
bmi.biWidth = w;
bmi.biHeight = h;
bmi.biPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info.
bmi.biBitCount = (short)bpp; // ie. 1bpp or 8bpp
bmi.biCompression = BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
bmi.biSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);
bmi.biXPelsPerMeter = 1000000; // not really important
bmi.biYPelsPerMeter = 1000000; // not really important
// Now for the colour table.
uint ncols = (uint)1 << bpp; // 2 colours for 1bpp; 256 colours for 8bpp
bmi.biClrUsed = ncols;
bmi.biClrImportant = ncols;
bmi.cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours
if (bpp == 1)
{
bmi.cols[0] = MAKERGB(0, 0, 0);
bmi.cols[1] = MAKERGB(255, 255, 255);
}
else
{
for (int i = 0; i < ncols; i++)
bmi.cols[i] = MAKERGB(i, i, i);
}
// For 8bpp we've created an palette with just greyscale colours.
// You can set up any palette you want here. Here are some possibilities:
// greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
// rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
// for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
// optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization
//
// Now create the indexed bitmap "hbm0"
IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
//
// Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
// GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
IntPtr sdc = GetDC(IntPtr.Zero); // First we obtain the DC for the screen
// Next, create a DC for the original hbitmap
IntPtr hdc = CreateCompatibleDC(sdc); SelectObject(hdc, hbm);
// and create a DC for the monochrome hbitmap
IntPtr hdc0 = CreateCompatibleDC(sdc); SelectObject(hdc0, hbm0);
// Now we can do the BitBlt:
BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
// Step (4): convert this monochrome hbitmap back into a Bitmap:
Bitmap b0 = Bitmap.FromHbitmap(hbm0);
//
// Finally some cleanup.
DeleteDC(hdc);
DeleteDC(hdc0);
ReleaseDC(IntPtr.Zero, sdc);
DeleteObject(hbm);
DeleteObject(hbm0);
//It have negative stride...
return b0;
}
According to documentation for the BITMAPINFOHEADER structure:
biHeight
Specifies the height of the bitmap, in pixels.
If biHeight is positive, the bitmap is a bottom-up DIB and its origin
is the lower left corner.
If biHeight is negative, the bitmap is a top-down DIB and its origin
is the upper left corner.
If biHeight is negative, indicating a top-down DIB, biCompression must
be either BI_RGB or BI_BITFIELDS. Top-down DIBs cannot be compressed.
So if you change the line in the code you posted to:
bmi.biHeight = -h;
Then it'll create a top-down bitmap with a positive stride.
--
There are other possibilities.
var newBmp = srcBmp.Clone(new Rectangle(0, 0, srcBmp.Width, srcBmp.Height), PixelFormat.Format8bppIndexed);
For me, that creates a bitmap that has a positive stride. But then, the original bitmap has positive stride. I don't know what it'll do if I call it on a bitmap with negative stride.
All that said, I'm wondering what problem you're really trying to solve. Why does it matter if the bitmap is bottom up or top down?

C# Capture screen to 8-bit (256 color) bitmap

I'm using this code to capture the screen:
public Bitmap CaptureWindow(IntPtr handle)
{
// get te hDC of the target window
IntPtr hdcSrc = User32.GetWindowDC(handle);
// get the size
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(handle, ref windowRect);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
// create a device context we can copy to
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
// select the bitmap object
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
// bitblt over
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
// restore selection
GDI32.SelectObject(hdcDest, hOld);
// clean up
GDI32.DeleteDC(hdcDest);
User32.ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
Bitmap img = Image.FromHbitmap(hBitmap);
// free up the Bitmap object
GDI32.DeleteObject(hBitmap);
return img;
}
I then want to convert the bitmap to 256 colors (8 bit). I tried this code but get an error about not being able to create an Image from an indexed bitmap format:
Bitmap img8bit = new Bitmap(img.Width,img.Height,
System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
Graphics g = Graphics.FromImage(img8bit);
g.DrawImage(img,new Point(0,0));
I did see some examples to convert bitmaps between different formats, but in my case I'm looking for the best way to do this while capturing from the screen. For example, if there is a method that will work better by creating an 8-bit bitmap to begin with and then blit the screen to that, that would be preferred over caputring screen to comptible bitmap first and then converting it. Unless it's better to capture then convert anyway.
I have a program written in C++ using Borland Builder 6.0 VCL, and I'm trying to memic that. In that case it is a simple matter of setting the pixel format for VCL's TBitmap object. I notice Bitmap.PixelFormat is read-only in .NET, ugh.
Update: In my case I don't think the answer is as complex as some other usage that requires figuring out the best palette entries, because Graphics.GetHalftonePalette using the screen DC should be fine, since my original bitmap comes from the screen, not just any random bitmap that might come from a file/email/download/etc. I beleive there is something that can be done with maybe 20 lines of code that involves DIBs and GetHalftonePalette -- just can't find it yet.
Converting a full color bitmap to 8bpp is a difficult operation. It requires creating a histogram of all the colors in the image and creating a palette that contains an optimized set of colors that best map to the original colors. Then using a technique like dithering or error diffusion to replace the pixels whose colors don't have an exact match with the palette.
This is best left to a professional graphics library, something like ImageTools. There is one cheap way that can be tricked in the .NET framework. You can use the GIF encoder, a file format that has 256 colors. The result isn't the greatest, it uses dithering and that can be pretty visible sometimes. Then again, if you really cared about image quality then you wouldn't use 8bpp anyway.
public static Bitmap ConvertTo8bpp(Image img) {
var ms = new System.IO.MemoryStream(); // Don't use using!!!
img.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
ms.Position = 0;
return new Bitmap(ms);
}
Capture the screen using a regular PixelFormat and then use Bitmap.Clone() to convert it to an optimized 256 indexed color like this:
public static Bitmap CaptureScreen256()
{
Rectangle bounds = SystemInformation.VirtualScreen;
using (Bitmap Temp = new Bitmap(bounds.Width, bounds.Height, PixelFormat.Format24bppRgb))
{
using (Graphics g = Graphics.FromImage(Temp))
{
g.CopyFromScreen(0, 0, 0, 0, Temp.Size);
}
return Temp.Clone(new Rectangle(0, 0, bounds.Width, bounds.Height), PixelFormat.Format8bppIndexed);
}
}

Is it possible to get a reference to the raw image buffer for system.drawing.graphics class?

I'm trying to render an image from an unmanaged control on to a WPF window. So far I'm able to get a working graphics object (because I'm able to overlay image to the unmanaged control). What I'm hoping to do is the opposite, which is to capture image from graphics object and save as a imagesource for another control.
var graphics = Graphics.FromHwnd(hwndPtr);//From image unmanaged source
graphics.(??) // save to bitmap or any image format
If not possible to save an image with graphics object directly, would it be possible to get a raw reference to the image buffer of the graphics object? (For use with code like below)
var bmp = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(
hwntPtr,120,120,format,stride,0);
Thanks in Advance..
This is slightly modified code from an MSDN forum article but it should do the trick as far as getting a System.Drawing.Bitmap from a System.Drawing.Graphics object.
[DllImport("coredll.dll", SetLastError = true)]
internal static extern bool BitBlt(IntPtr hDest, int nDstX, int nDstY, int nWidth, int nHeight, IntPtr hSrc, int nSrcX, int nSrcY, uint BitBltOperation);
Bitmap SaveAsBitmap(Graphics gxSrc, int sizeX, int SizeY)
{
Bitmap bm = new Bitmap(sizeX, sizeY);
Graphics gxDst = Graphics.FromImage(bm);
IntPtr hSrc = gxSrc.GetHdc();
IntPtr hDst = gxDst.GetHdc();
BitBlt(hDst, 0, 0, sizeX, sizeY, hSrc, 0, 0, 0xcc0020); // SourceCopy operation
gxDst.ReleaseHdc(hDst);
gxSrc.ReleaseHdc(hSrc);
gxDst.Dispose();
return bm;
}
Original article

Is there a way to use AwesomiumSharp to create a System.Drawing.Image in memory?

Background is, I'm using XNA, and I render Awesomium to an Image, which I then make a Texture2D from.
The code to render Awesomium to an Image via a file looks something like this:
webView.Render().SaveToPNG("awesomium.png", true);
var image = Image.FromFile("awesomium.png", true);
Which works fine, but it's dog slow (as you can imagine).
Is there a way to use Awesomium to render to a System.Drawing.Image without writing out to the filesystem?
In the end I found my answer in awesomiumdotnet. I guess the official wrapper isn't always the most complete :/
public static class Rbex
{
public static Bitmap ToBitmap(this RenderBuffer buffer)
{
const int depth = 4;
const PixelFormat pf = PixelFormat.Format32bppArgb;
// Create bitmap
Bitmap bitmap = new Bitmap(buffer.GetWidth(), buffer.GetHeight(), pf);
BitmapData data = bitmap.LockBits(new Rectangle(0,0, buffer.GetWidth(), buffer.GetHeight()), ImageLockMode.WriteOnly, bitmap.PixelFormat);
buffer.CopyTo(data.Scan0, buffer.GetWidth() * depth, depth, false);
bitmap.UnlockBits(data);
return bitmap;
}
}

Create Bitmap from a byte array of pixel data

This question is about how to read/write, allocate and manage the pixel data of a Bitmap.
Here is an example of how to allocate a byte array (managed memory) for pixel data and creating a Bitmap using it:
Size size = new Size(800, 600);
PixelFormat pxFormat = PixelFormat.Format8bppIndexed;
//Get the stride, in this case it will have the same length of the width.
//Because the image Pixel format is 1 Byte/pixel.
//Usually stride = "ByterPerPixel"*Width
//But it is not always true. More info at bobpowell.
int stride = GetStride(size.Width, pxFormat);
byte[] data = new byte[stride * size.Height];
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
Bitmap bmp = new Bitmap(size.Width, size.Height, stride,
pxFormat, handle.AddrOfPinnedObject());
//After doing your stuff, free the Bitmap and unpin the array.
bmp.Dispose();
handle.Free();
public static int GetStride(int width, PixelFormat pxFormat)
{
//float bitsPerPixel = System.Drawing.Image.GetPixelFormatSize(format);
int bitsPerPixel = ((int)pxFormat >> 8) & 0xFF;
//Number of bits used to store the image data per line (only the valid data)
int validBitsPerLine = width * bitsPerPixel;
//4 bytes for every int32 (32 bits)
int stride = ((validBitsPerLine + 31) / 32) * 4;
return stride;
}
I thought that the Bitmap would make a copy of the array data, but it actually points to the same data. Was you can see:
Color c;
c = bmp.GetPixel(0, 0);
Console.WriteLine("Color before: " + c.ToString());
//Prints: Color before: Color [A=255, R=0, G=0, B=0]
data[0] = 255;
c = bmp.GetPixel(0, 0);
Console.WriteLine("Color after: " + c.ToString());
//Prints: Color after: Color [A=255, R=255, G=255, B=255]
Questions:
Is it safe to do create a bitmap from a byte[] array (managed memory) and free() the GCHandle? If it is not safe, Ill need to keep a pinned array, how bad is that to GC/Performance?
Is it safe to change the data (ex: data[0] = 255;)?
The address of a Scan0 can be changed by the GC? I mean, I get the Scan0 from a locked bitmap, then unlock it and after some time lock it again, the Scan0 can be different?
What is the purpose of ImageLockMode.UserInputBuffer in the LockBits method? It is very hard to find info about that! MSDN do not explain it clearly!
EDIT 1: Some followup
You need to keep it pinned. Will it slow down the GC? I've asked it here. It depends on the number of images and its sizes. Nobody have gave me a quantitative answer. It seams that it is hard to determine.
You can also alloc the memory using Marshal or use the unmanaged memory allocated by the Bitmap.
I've done a lot of test using two threads. As long as the Bitmap is locked it is ok. If the Bitmap is unlock, than it is not safe! My related post about read/write directly to Scan0. Boing's answer "I already explained above why you are lucky to be able to use scan0 outside the lock. Because you use the original bmp PixelFormat and that GDI is optimized in that case to give you the pointer and not a copy. This pointer is valid until the OS will decide to free it. The only time there is a guarantee is between LockBits and UnLockBits. Period."
Yeah, it can happen, but large memory regions are treated different by the GC, it moves/frees this large object less frequently. So it can take a while to GC move this array. From MSDN: "Any allocation greater than or equal to 85,000 bytes goes on the large object heap (LOH)" ... "LOH is only collected during a generation 2 collection". .NET 4.5 have Improvements in LOH.
This question have been answered by #Boing. But I'm going to admit. I did not fully understand it. So if Boing or someone else could please clarify it, I would be glad. By the way, Why I can't just directly read/write to Sca0 without locking? => You should not write directly to Scan0 because Scan0 points to a copy of the Bitmap data made by the unmanaged memory (inside GDI). After unlock, this memory can be reallocate to other stuff, its not certain anymore that Scan0 will point to the actual Bitmap data. This can be reproduced getting the Scan0 in a lock, unlock, and do some rotate-flit in the unlocked bitmap. After some time, Scan0 will point to an invalid region and you will get an exception when trying to read/write to its memory location.
Its safe if you marshal.copy data rather than setting scan0 (directly or via that overload of BitMap()). You don't want to keep managed objects pinned, this will constrain the garbage collector.
If you copy, perfectly safe.
The input array is managed and can be moved by the GC, scan0 is an unmanaged pointer that would get out of date if the array moved. The Bitmap object itself is managed but sets the scan0 pointer in Windows via a handle.
ImageLockMode.UserInputBuffer is? Apparently it can be passed to LockBits, maybe it tells Bitmap() to copy the input array data.
Example code to create a greyscale bitmap from array:
var b = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
ColorPalette ncp = b.Palette;
for (int i = 0; i < 256; i++)
ncp.Entries[i] = Color.FromArgb(255, i, i, i);
b.Palette = ncp;
var BoundsRect = new Rectangle(0, 0, Width, Height);
BitmapData bmpData = b.LockBits(BoundsRect,
ImageLockMode.WriteOnly,
b.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpData.Stride*b.Height;
var rgbValues = new byte[bytes];
// fill in rgbValues, e.g. with a for loop over an input array
Marshal.Copy(rgbValues, 0, ptr, bytes);
b.UnlockBits(bmpData);
return b;
Concerning your question 4: The ImageLockMode.UserInputBuffer can give you the control of the allocating process of those huge amount of memory that could be referenced into a BitmapData object.
If you choose to create yourself the BitmapData object you can avoid a Marshall.Copy. You will then have to use this flag in combinaison with another ImageLockMode.
Beware that it is a complicated business, specially concerning Stride
and PixelFormat.
Here is an example that would get in one shot the content of 24bbp buffer onto a BitMap and then in one another shot read it back into another buffer into 48bbp.
Size size = Image.Size;
Bitmap bitmap = Image;
// myPrewrittenBuff is allocated just like myReadingBuffer below (skipped for space sake)
// But with two differences: the buff would be byte [] (not ushort[]) and the Stride == 3 * size.Width (not 6 * ...) because we build a 24bpp not 48bpp
BitmapData writerBuff= bm.LockBits(new Rectangle(0, 0, size.Width, size.Height), ImageLockMode.UserInputBuffer | ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb, myPrewrittenBuff);
// note here writerBuff and myPrewrittenBuff are the same reference
bitmap.UnlockBits(writerBuff);
// done. bitmap updated , no marshal needed to copy myPrewrittenBuff
// Now lets read back the bitmap into another format...
BitmapData myReadingBuffer = new BitmapData();
ushort[] buff = new ushort[(3 * size.Width) * size.Height]; // ;Marshal.AllocHGlobal() if you want
GCHandle handle= GCHandle.Alloc(buff, GCHandleType.Pinned);
myReadingBuffer.Scan0 = Marshal.UnsafeAddrOfPinnedArrayElement(buff, 0);
myReadingBuffer.Height = size.Height;
myReadingBuffer.Width = size.Width;
myReadingBuffer.PixelFormat = PixelFormat.Format48bppRgb;
myReadingBuffer.Stride = 6 * size.Width;
// now read into that buff
BitmapData result = bitmap.LockBits(new Rectangle(0, 0, size.Width, size.Height), ImageLockMode.UserInputBuffer | ImageLockMode.ReadOnly, PixelFormat.Format48bppRgb, myReadingBuffer);
if (object.ReferenceEquals(result, myReadingBuffer)) {
// Note: we pass here
// and buff is filled
}
bitmap.UnlockBits(result);
handle.Free();
// use buff at will...
If you use ILSpy you'll see that this method link to GDI+ and those methods helps are more complete.
You may increase performance by using your own memory scheme, but
beware that Stride may need to have some alignment to get the best
performance.
You then will be able to go wild for example allocating huge virtual memory mapped scan0 and blit them quite efficiently.
Note that pinning huge array (and especially a few) won't be a burden to the GC and will allow you to manipulate the byte/short in a totally safe way (or unsafe if you seek speed)
I'm not sure if there is a reason you're doing it the way you are. Maybe there is. It seems like you're off the beaten path enough so that you might be trying to do something more advanced than what the title of your question implies...
However, the traditional way of creating a Bitmap from a Byte array is:
using (MemoryStream stream = new MemoryStream(byteArray))
{
Bitmap bmp = new Bitmap(stream);
// use bmp here....
}
Here is a sample code i wrote to convert byte array of pixels to an 8 bits grey scale image(bmp)
this method accepts the pixel array, image width, and height as arguments
//
public Bitmap Convert2Bitmap(byte[] DATA, int width, int height)
{
Bitmap Bm = new Bitmap(width,height,PixelFormat.Format24bppRgb);
var b = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
ColorPalette ncp = b.Palette;
for (int i = 0; i < 256; i++)
ncp.Entries[i] = Color.FromArgb(255, i, i, i);
b.Palette = ncp;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int Value = DATA[x + (y * width)];
Color C = ncp.Entries[Value];
Bm.SetPixel(x,y,C);
}
}
return Bm;
}

Categories

Resources