I'm looking for a simple way to implement a framebuffer in C#, D or Java. Something (an API or library) that would allow me to work with a 2d array of colors and update individual pixels or areas. Also, something that doesn't incur large overhead on update. I know that this can be done with OpenGL, but the API seems far too complicated for what I'm doing.
Try using a plain old System.Drawing.Bitmap in .NET?
You could use Bitmap.Lockbits() to get access to the byte array behind the bitmap and update it. This is much more efficient than normal pixel operations on the bitmap.
MSDN has an example here that I've pasted from:
private void LockUnlockBitsExample(PaintEventArgs e)
{
// Create a new bitmap.
Bitmap bmp = new Bitmap("c:\\fakePhoto.jpg");
// Lock the bitmap's bits.
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
System.Drawing.Imaging.BitmapData bmpData =
bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat);
// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;
// Declare an array to hold the bytes of the bitmap.
int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
byte[] rgbValues = new byte[bytes];
// Copy the RGB values into the array.
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
// Set every third value to 255. A 24bpp bitmap will look red.
for (int counter = 2; counter < rgbValues.Length; counter += 3)
rgbValues[counter] = 255;
// Copy the RGB values back to the bitmap
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
// Unlock the bits.
bmp.UnlockBits(bmpData);
// Draw the modified image.
e.Graphics.DrawImage(bmp, 0, 150);
}
Arrays will take lots of time while iterating through such large amount pixel data for a complete screen. It will be better to find something that doesn't needs or need very less amount of iteration. Something more like pointers in C.
If what you need is a 2D array, In C# you can create a multidimensional array that gives you direct access to every member. To make this efficient, try to avoid frequent boxing and unboxing and don't allocate and deallocate large memory chunks frequently, and if you do it right there's no reason why this task would be much less efficient in C# or Java than in other languages.
Related
This question already has answers here:
convert array of bytes to bitmapimage
(2 answers)
Closed 8 years ago.
I have a scanner that delivers Grayscale 8 bit images. My goal is to convert this image to Monochrome, which I already have implemented. To convert i need to operate on bitmap objects, rather than just the byte array. My code to get the Bitmap from the byte array looks like this:
public static Bitmap ByteArrayToBitmap(byte[] data, int width, int height)
{
Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
bmp.Save(#"C:\Type7Test\TestImage.bmp", ImageFormat.Bmp);
return bmp;
}
My problem is that from this point the bitmap is getting formatted the wrong way. Notice the line where I save the bitmap to disk(just for testing), this is the result:
As you can see this is not the expected image. I expect this as the result:
I suspect that there is something wrong with the PixelFormats and the creation of the bitmap. So can anyone pin-point me in the right direction to create a proper Bitmap from an array of raw image data?
After thinking about it, are you sure that PixelFormat.Format8bppIndexed is a correct format? The Bitmap is created and it - more or less - looks like the desired image. That shows that you give too much or too little data for a single picture - which means that your PixelFormat may be wrong.
You can try to experiment with different types and check which one works. They wary, dependent on how many color does your image has (the more colors there are, the bigger bites-per-pixel ratio you need).
I am doing some image processing on a custom class representing 16 bit gray-scale images.
The intensity of the pixels is stored in a single dimension ushort array: ushort[] data
I also have the width, the height, the dpi, the stride, if necessary.
My motivation is the following: I show the results of certain operations quite fast, but the conversion from the array to the bitmapsource to the image object is too lengthy, so I was thinking of an Image object which drew its "source" directly from the array. I could thus write a method "update()" instead of doing multiple conversions.
1/ Is this possible?
2/ Would it be faster?
3/ How would I go about doing it?
The way I currently draw the image is with the following code (there's a bit more to that, but essetially that the heart of it)
BitmapSource bmps = BitmapSource.Create(Width, Height, Dpi, Dpi, PixelFormats.Gray16, null,
data, stride);
image.Source=bmps;
Thank you!
Instead of using a separate array to store pixels and then creating a BitmapSource to show the results, I think it would be better using a WriteableBitmap, this way you can store pixel data (16-bit greyscale values) directly in its BackBuffer. Thus you can code something like this:
// You create "bmp" just once and then update its content when needed
var bmp = new WriteableBitmap(640, 480, 96, 96, PixelFormats.Gray16, null);
var imgRect = new Int32Rect(0, 0, bmp.PixelWidth, bmp.PixelHeight);
Then you update the image:
bmp.Lock();
ApplySomeFilter(bmp.BackBuffer, ImageFilter.Blur); // this is just an example ;)
bmp.AddDirtyRect(imgRect);
bmp.Unlock();
The ApplySomeFilter method can use unsafe code to modify pixel data of the WriteableBitmap.
For example, if ApplySomeFilter is defined like this:
unsafe public void ApplySomeFilter(void* imgBuffer, ImageFilter filter)
{
// code that modifies pixels goes here
}
then you can call it this way:
ApplySomeFilter(bmp.BackBuffer.ToPointer(), ImageFilter.Blur);
Background:
I'm working with an external (unmanaged) API which will render an image for me and pass the memory location of the image(as well as stride, etc.) back.
This API is not yet fully developed, so I'm trying to mock the behaviour for the time being.
Task:
I'm trying to create an image from part of the image which is created in the unmanaged code.
For example, I would like to create a BitmapSource which contains only the right half of the image.
To simluate this, I create an image in memory:
Bitmap logo = Properties.Resources.test2;
BitmapData bmpData =
logo.LockBits(new System.Drawing.Rectangle(0, 0, 970, 844), System.Drawing.Imaging.ImageLockMode.ReadWrite, logo.PixelFormat);
Now I can get the starting point of the image in memory with
IntPtr startOfImage = bmpData.Scan0;
To get the right hand side of the image, i'm unsuccessfully trying (assuming the image dimensions are 970*844 and the Pixelformat is 24bit RGB):
IntPtr locationOfImage = bmpData.Scan0 += bmpData.Stride / 2 ;
BitmapSource source = BitmapImage.Create(485, 844, 96, 96, System.Windows.Media.PixelFormats.Rgb24, null, locationOfImage, 844 * bmpData.Stride / 2, bmpData.Stride / 2);
However, this is no way is giving me the right hand side of the image, what am I missing?
Best Whishes
Daniel
EDIT:
It seems clear that this is not returning the right hand side of the image because only for the first line I am skipping the half of the stride.
Is it somehow possible to create an image while always skipping part of the bytes (e.g. have a skip value for on each line skipping a part of the line)?
Obviously, I also could first read in all necessary bytes into an array and pass it instead of the IntPtr, however it would be nice to be able to directly pass the location.
I have some code that copies a Bitmap into a Direct3D Texture for rendering video. When the system is under heavy load, I get occasional AccessViolationException's on the call to Bitmap.LockBits.
Here is a very simple example of what happens:
// Get a PixelFormat.Format24bppRgb image
Bitmap bitmap24 = getVideoFrame();
// Copy into a 32bpp ARGB texture
BitmapData bitmapData = null;
try
{
// Why am I allowed to do this?
bitmapData = this.bitmap24.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadOnly,
PixelFormat.Format32bppArgb);
// Blit from bitmapData to texture32
Texture texture32 = convertBitmapToTexture(bitmapData);
}
finally
{
if (bitmapData != null)
this.bitmap.UnlockBits(bitmapData);
}
Why am I allowed to call Bitmap.LockBits and pass in PixelFormat.Format32bppArgb when I know that the bitmap format is PixelFormat.Format24bppRgb? I assume that LockBits must automagically expand from 3 bytes per pixel to 4.
Could the LockBits expansion be causing these exceptions? Why would it only happen occasionaly?
LockBits will store the bitmap in memory in whatever format you specify.
AccessViolationException occurs when you're accessing memory you aren't supposed to be accessing. I can think of two scenarios where this will happen:
1) You're using ImageLockMode.ReadOnly.. is there anywhere you're actually trying to change a value of a pixel? Perhaps try changing this to ImageLockMode.ReadWrite and see if that helps.
2) You've confused yourself about how big your buffer must be and you have a Marshal.Copy in your convertBitmapToTexture function that is attempting to copy past the end of your buffer.
It might be easier for you to think in terms of int's instead of bytes when you're using 32bppArgb. That way you'll be less likely to run into any confusion.
This is a follow up from Rendering to a single Bitmap object from multiple threads
What im trying to achieve is to take a bitmap of say 50x50 pixels and draw it onto a larger bitmap(100x100 pixels) at any point on the larger image, using the bitmaps LockBits function or any other but NOT graphics.DrawImage. My reasons for not wanting to use DrawImage is stated in the other thread.
I have managed to get something by using Marshal.Copy from the source BitmapData to the dest BitmapData but its creating a tiled, stretched image horizontally.
You could manipulate the image in memory without relying on any system calls. If you dig into the underlying format of a .BMP file you could build your own Device Independant Bitmap class that truly "understands" the low level format of a .BMP.
For example a 8 bit per pixel image is essentially a 2 dimensional array of bytes (each byte is 1 pixel) plus a simple color table. Roughly speaking (and this is very very rough):
byte[,] bColors = new byte[3,256]; // 256 RGB colors
byte[,] bImage = new byte[25,50]; // 25 x 50 pixels
The trick is (as always) getting a hold of the raw pixel data, doing the processing, and then updating the raw pixel data with your changes.
In the past I've approached this by converting a GDI HBITMAP into a 24bpp DIB, doing my funky image processing on the raw pixels (3 bytes per pixels makes this easier), then converting the DIB back into a HBITMAP. This was all using just classic GDI (pre GDI+ even, let alone C#).
Using that approach you could design a control structure to allow multiple writers to different sections of your much bigger image.
However... the lowlevel BitBlt GDI calls are likely to be way more efficient that anything you can do. If I were you I'd make certain that just doing 50 or 100 bitblt's in a row would be too slow (you'd likely need to do this in c++).
The most annoying challenges with dealing with DIB's are:
Converting a DIB to an actual "image" ready for display and
Converting an actual "image" into a DIB
Saving a DIB as something other than a .BMP
Core references when I started learning the "horror" that images actually are:
http://msdn.microsoft.com/en-us/library/dd183562(VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd144879(VS.85).aspx
http://msdn.microsoft.com/en-us/library/dd162973(VS.85).aspx
How you go about getting to/from .NET Image's... well... that's a good question :)
This should work just fine using LockBits/BitmapData, if you are using a 32bpp [P]ARGB pixel format. The trick is that you will have to copy the data one row at a time so that it aligns in the correct places. You should be able to do this using something like:
Rectangle srcArea = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height);
BitmapData srcData = srcBitmap.LockBits(srcArea, ImageLockMode.ReadOnly, destBitmap.PixelFormat);
Rectangle destArea = new Rectangle(25, 25, srcBitmap.Width, srcBitmap.Height);
BitmapData destData = destBitmap.LockBits(destArea, ImageLockMode.WriteOnly, destBitmap.PixelFormat);
IntPtr srcPtr = srcData.Scan0;
IntPtr destPtr = destData.Scan0;
byte[] buffer = new byte[srcData.Stride];
for (int i = 0; i < srcData.Height; ++i)
{
Marshal.Copy(srcPtr, buffer, 0, buffer.Length);
Marshal.Copy(buffer, 0, destPtr, buffer.Length);
srcPtr += srcData.Stride;
destPtr += destData.Stride;
}
srcBitmap.UnlockBits(srcData);
destBitmap.UnlockBits(destData);
As a warning, this code won't work as is because I am not sure what the right incantations are for incrementing IntPtr's. I've done this same type of thing before, but in C++. Also, I don't know if there is a way to directly copy the data instead of using an intermediate buffer.
An additional caveat: the LockBits call srcBitmap and the sizing of the buffer assume that srcBitmap will be completely enclosed in destBitmap. If this is not the case (some part of the bitmap will be cropped off) the area locked and the size of the buffer will need to be adjusted.
If you are not using a 32bpp pixel format (ie 24bpp), it will be more difficult. The stride of your source BitmapData may include some amount of padding that should not be copied. You could work around this by calculating the amount of actual pixel data in a source row, and copy this amount. Indexed pixel formats would be even more work.
I would recommend taking a look at the internal bitmap memory structure.
The best approach, I think, would be to not try to set the BitmapData directly. Instead, I would make a single, shared byte array of the appropriate size, and set the byte array directly from your smaller images.
When you compose your larger image, you can take the final byte array and directly make a Bitmap from the byte data.
This has the advantage of allowing you to control the memory management, thread the operations, etc, as you seemed to want to do in your original post. It should be very fast for the data access, as well.