I am using the below code to extract RGB values from images, sometimes this works, however on certain files (seemingly where the Stride is not divisible by the width of the bitmap) it is returning mixed up values:
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rect, Imaging.ImageLockMode.ReadOnly, Imaging.PixelFormat.Format24bppRgb)
Dim ptr As IntPtr = bmpData.Scan0
Dim cols As New List(Of Color)
Dim bytes As Integer = Math.Abs(bmpData.Stride) * bmp.Height
Dim rgbValues(bytes - 1) As Byte
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes)
' Retrieve RGB values
For i = modByte To rgbValues.Length Step 3
cols.Add(Color.FromArgb(rgbValues(i + 2), rgbValues(i + 1), rgbValues(i)))
Next
bmp.UnlockBits(bmpData)
bmp.Dispose()
Dim colsCnt As List(Of RgbPixels) = cols.GroupBy(Function(g) New With {Key .R = g.R, Key .G = g.G, Key .B = g.B}).Select(Function(s) New RgbPixels With {.Colour = Color.FromArgb(s.Key.R, s.Key.G, s.Key.B), .Amount = s.Count()}).ToList()
After grouping the resulting colours, the values are something like:
R G B
255 255 255
255 255 0
255 0 0
0 0 255
0 255 255
Or some variation of that, when they should just be:
R G B
255 255 255
0 0 0
Please point me in the right direction, BTW my source bmp is in PixelFormat.Format24bppRgb too, so I don't believe that is the problem. Also if you can only answer in C# that is not a problem.
The problem is that you're not considering the stride value. Stride is always padded so that the width of the byte-array per image row is dividable by 4. This is an optimization related to memory copy and how the CPU works, that goes decades back and still is useful.
F.ex, if one image has a width of 13 pixels, the stride would be like this (simplified to one component):
============= (width 13 pixels = 13 bytes when using RGB)
================ (stride would be 16)
for an image of 14 pixels it would look like this:
============== (width 14 pixels = 14 bytes when using RGB)
================ (stride would still be 16)
So in your code you need to handle a stride row instead of a byte array, unless you are using fixed and defined widths of the images.
I modified your code so it skips rows by stride:
Dim rect As New Rectangle(0, 0, bmp.Width, bmp.Height)
Dim bmpData As System.Drawing.Imaging.BitmapData = bmp.LockBits(rect, Imaging.ImageLockMode.ReadOnly, Imaging.PixelFormat.Format24bppRgb)
Dim ptr As IntPtr = bmpData.Scan0
Dim cols As New List(Of Color)
Dim bytes As Integer = Math.Abs(bmpData.Stride) * bmp.Height
Dim rgbValues(bytes - 1) As Byte
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes)
Dim x, y, dx, l as Integer
For y = 0 To rect.Height - 1
l = y * bmpData.Stride 'calulate line based on stride
For x = 0 To rect.Width - 1
dx = l + x * 3 '3 for RGB, 4 for ARGB, notice l is used as offset
cols.Add(Color.FromArgb(rgbValues(dx + 2), _
rgbValues(dx + 1), _
rgbValues(dx)))
Next
Next
' Retrieve RGB values
'For i = modByte To rgbValues.Length Step 3
' cols.Add(Color.FromArgb(rgbValues(i + 2), rgbValues(i + 1), rgbValues(i)))
'Next
bmp.UnlockBits(bmpData)
bmp.Dispose()
Related
I'm writing in c# and using this to fill my PictureBox with a byte array
var bmp = new Bitmap(48, 32, PixelFormat.Format1bppIndexed);
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
Marshal.Copy(fileArray, 0, bmpData.Scan0, fileArray.Length);
bmp.UnlockBits(bmpData);
return bmp;
The result is very strange.
It only fills up to the 125th byte. So I tried playing with the data and the 0-125 bytes show as they should but anything after byte 125 is not shown and it overwrites byte 125.
So if I do 0-125 all 0xff I get as solid bar up top. but adding 126 as 0x00 it replaces 125 with 0x00.
Instead of inserting the padding into your data you can move the data row by row:
int w = 48;
int h = 32;
int unpadded = w / 8; // unpadded byte length of one line in your data
Bitmap bmp = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
ImageLockMode.WriteOnly, bmp.PixelFormat);
int stride = 8 // padded length of scanline
for (int i = 0; i < h; i++)
{
Marshal.Copy(blob, i * unpadded , bmpData.Scan0 + i * stride, unpadded );
}
bmp.UnlockBits(bmpData);
return bmp;
Note that to make bitmap operations faster all physical rows must have a length that's a multiple of 4 bytes. The resulting, padded length is called stride.
The stride is the width of a single row of pixels (a scan line),
rounded up to a four-byte boundary. If the stride is positive, the
bitmap is top-down. If the stride is negative, the bitmap is
bottom-up.
With a logical length of 48 pixels and 1bpp your logical length is 6 bytes, so it must be padded to 8 bytes internally.
Best not to move your data in managed code to prepare this but to move only the right portions to the right slots..
Here are more examples of calculating stride for other formats
This solution worked for me. Its by far not the best and shows a lack of understanding or simply bitmaps can not support monochrome 1 bit per pixel images. Never the less it works. All this does is adds 2 bytes on each row of 6 bytes. So that the stride can deal with it.
int j = 1;
int k = 0;
for (int i = 0; i < 192; i++)
{
fixArray[k] = blob[i];
j++;
if (j == 7)
{
j = 1;
fixArray[++k] = 0;
fixArray[++k] = 0;
}
k++;
}
As the subject says, I have a .bmp image and I need to write a code which will be able to get the color of any pixel of the image. It is a 1bpp (indexed) image, so the colour will be either black or white. Here is the code I currently have:
//This method locks the bits of line of pixels
private BitmapData LockLine(Bitmap bmp, int y)
{
Rectangle lineRect = new Rectangle(0, y, bmp.Width, 1);
BitmapData line = bmp.LockBits(lineRect,
ImageLockMode.ReadWrite,
bmp.PixelFormat);
return line;
}
//This method takes the BitmapData of a line of pixels
//and returns the color of one which has the needed x coordinate
private Color GetPixelColor(BitmapData data, int x)
{
//I am not sure if this line is correct
IntPtr pPixel = data.Scan0 + x;
//The following code works for the 24bpp image:
byte[] rgbValues = new byte[3];
System.Runtime.InteropServices.Marshal.Copy(pPixel, rgbValues, 0, 3);
return Color.FromArgb(rgbValues[2], rgbValues[1], rgbValues[0]);
}
But how can I make it work for a 1bpp image? If I read only one byte from the pointer it always has the 255 value, so I assume, I am doing something wrong.
Please, do not suggest to use the System.Drawing.Bitmap.GetPixel method, because it works too slow and I want the code to work as fast as possible.
Thanks in advance.
EDIT:
Here is the code that works fine, just in case someone needs this:
private Color GetPixelColor(BitmapData data, int x)
{
int byteIndex = x / 8;
int bitIndex = x % 8;
IntPtr pFirstPixel = data.Scan0+byteIndex;
byte[] color = new byte[1];
System.Runtime.InteropServices.Marshal.Copy(pFirstPixel, color, 0, 1);
BitArray bits = new BitArray(color);
return bits.Get(bitIndex) ? Color.Black : Color.White;
}
Ok, got it! You need to read the bits from the BitmapData and apply a mask to the bit you want extract the color:
var bm = new Bitmap...
//lock all image bits
var bitmapData = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
// this will return the pixel index in the color pallete
// since is 1bpp it will return 0 or 1
int pixelColorIndex = GetIndexedPixel(50, 30, bitmapData);
// read the color from pallete
Color pixelColor = bm.Pallete.Entries[pixelColorIndex];
And here is the method:
// x, y relative to the locked area
private int GetIndexedPixel(int x, int y, BitmapData bitmapData)
{
var index = y * bitmapData.Stride + (x >> 3);
var chunk = Marshal.ReadByte(bitmapData.Scan0, index);
var mask = (byte)(0x80 >> (x & 0x7));
return (chunk & mask) == mask ? 1 : 0;
}
The pixel position is calculated in 2 rounds:
1) Find the byte where pixel in 'x' is (x / 8): each byte holds 8 pixels, to find the byte divide x/8 rounding down: 58 >> 3 = 7, the pixel is on the byte 7 of the current row (stride)
2) Find the bit on the current byte (x % 8): Do x & 0x7 to get only the 3 leftmost bits (x % 8)
Example:
x = 58
// x / 8 - the pixel is on byte 7
byte = 58 >> 3 = 58 / 8 = 7
// x % 8 - byte 7, bit 2
bitPosition = 58 & 0x7 = 2
// the pixels are read from left to right, so we start with 0x80 and then shift right.
mask = 0x80 >> bitPosition = 1000 0000b >> 2 = 0010 0000b
First of all, if you need to read a single pixel in one operation, then GetPixel will be equivalent in performance. The expensive operation is locking the bits, ie. you should hold on to the BitmapData for doing all the reading you need, and only close it at the end - but remember to close it !
There seems to be some confusion about your pixel format, but let's assume it is correct 1bpp. Then each pixel will occupy one bit, and there will be data for 8 pixels in a byte. Therefore, your indexing calculation is incorrect. The location of the byte would be in x/8, then you need to take bit x%8.
I wrote the code below in order to manipulate the color of an image. I want to somehow rip apart each pixel of the image. So for each pixel, i want access to the 5 bits of red, 6 bits of green and 5 bits of blue (as per 16 bit images). How would i change my code to do this? I guess i would have to somehow convert those byte values which i'm setting to bits?
Any help would be great.
private Bitmap InvertBitmap(Bitmap bmp)
{
unsafe
{
//create an empty bitmap the same size as original
Bitmap newBitmap = new Bitmap(bmp.Width, bmp.Height);
//lock the original bitmap in memory
System.Drawing.Imaging.BitmapData originalData = bmp.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
//lock the new bitmap in memory
System.Drawing.Imaging.BitmapData newData = newBitmap.LockBits(
new Rectangle(0, 0, bmp.Width, bmp.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
//set the number of bytes per pixel
int pixelSize = 3;
for (int y = 0; y < bmp.Height; y++)
{
//get the data from the original image
byte* originalImageRow = (byte*)originalData.Scan0 + (y * originalData.Stride);
//get the data from the new image
byte* newImageRow = (byte*)newData.Scan0 + (y * newData.Stride);
for (int x = 0; x < bmp.Width; x++)
{
//set the new image's pixel to the inverted version
newImageRow[x * pixelSize] = (byte)(255 - originalImageRow[x * pixelSize + 0]); //B
newImageRow[x * pixelSize + 1] = (byte)(255 - originalImageRow[x * pixelSize + 1]); //G
newImageRow[x * pixelSize + 2] = (byte)(255 - originalImageRow[x * pixelSize + 2]); //R
}
}
//unlock the bitmaps
newBitmap.UnlockBits(newData);
bmp.UnlockBits(originalData);
return newBitmap;
}
}
If you have a 16-bit integer x, you can extract ranges of bits within it by first masking those bits with a binary AND, then shifting the result. Like so:
int x = 33808; // 1000010000010000, for testing
int r = (x & 63488) >> 11; // 63488 = 1111100000000000
int g = (x & 2016) >> 5; // 2016 = 0000011111100000
int b = (x & 31); // 31 = 0000000000011111
// r = 10000
// g = 100000
// b = 10000
I hope that helps.
RGB24 is 1 byte per color channel so you don't need to do any bit twiddling to extract them from the data you already have. "getting the bits" doesn't really make sense as you can set their values already e.g.
newImageRow[x * pixelSize] = (byte)(originalImageRow[x * pixelSize + 0] | 0x80); //B
will set the new image blue channel to the original image blue channel but will set the high order bit to 1.
newImageRow[x * pixelSize] = (byte)(originalImageRow[x * pixelSize + 0] ^ 0xFF); //B
will invert the channel.
So you really just need to use bitwise operators (| & >> << ^)on the data you already have.
I am creating a program that scans all the pixels of an image, and whenever it finds a pixel that contains the color pink. It makes the pixel black. But it doesn't seem to find a pink pixel when there is two of them on the image. I do not know if I am using LockBits correctly, maybe I am using it wrong. Can someone please help me solve this I would greatly appreciate it.
Here is the code below:
Bitmap bitmap = pictureBox1.Image as Bitmap;
System.Drawing.Imaging.BitmapData d = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, bitmap.PixelFormat);
IntPtr ptr = d.Scan0;
byte[] rgbs = new byte[Math.Abs(d.Stride) * bitmap.Height];
Marshal.Copy(ptr, rgbs, 0, rgbs.Length);
Graphics g = pictureBox1.CreateGraphics();
for (int index = 2; index < rgbs.Length; index += 3)
{
if (rgbs[index] == 255 && rgbs[index - 1] == 0 && rgbs[index - 2] == 255) // If color = RGB(255, 0, 255) Then ...
{
// This never gets executed!
rgbs[index] = 0;
rgbs[index - 1] = 0;
rgbs[index - 2] = 0;
}
}
Marshal.Copy(rgbs, 0, ptr, rgbs.Length); // Copy rgb values back to the memory location of the bitmap.
pictureBox1.Image = bitmap;
bitmap.UnlockBits(d);
You don't need to copy the pixel data into an array. The point of LockBits is it gives you direct (unsafe) access the memory. You can just iterate the pixels and change them as you find them. You will need to know the format of the image to do this successfully.
BitmapData bmd=bm.LockBits(new Rectangle(0, 0, 10, 10),
ImageLockMode.ReadOnly, bm.PixelFormat);
// Blue, Green, Red, Alpha (Format32BppArgb)
int pixelSize=4;
for(int y=0; y<bmd.Height; y++)
{
byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);
for(int x=0; x<bmd.Width; x++)
{
int offSet = x*pixelSize;
// read pixels
byte blue = row[offSet];
byte green = row[offSet+1];
byte red = row[offSet+2];
byte alpha = row[offSet+3];
// set blue pixel
row[x*pixelSize]=255;
}
}
It's a little more tricky in VB than C# as VB has no knowledge of pointers and requires the use of the marshal class to access unmanaged data. Here's some sample code. (For some reason I originally though this was a VB question).
Dim x As Integer
Dim y As Integer
' Blue, Green, Red, Alpha (Format32BppArgb)
Dim PixelSize As Integer = 4
Dim bmd As BitmapData = bm.LockBits(new Rectangle(0, 0, 10, 10),
ImageLockMode.ReadOnly, bm.PixelFormat)
For y = 0 To bmd.Height - 1
For x = 0 To bmd.Width - 1
Dim offSet As Int32 = (bmd.Stride * y) + (4 * x)
' read pixel data
Dim blue As Byte = Marshal.ReadByte(bmd.Scan0, offSet)
Dim green As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 1)
Dim red As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 2)
Dim alpha As Byte = Marshal.ReadByte(bmd.Scan0, offSet + 3)
' set blue pixel
Marshal.WriteByte(bmd.Scan0, offSet , 255)
Next
Next
I need to access each pixel of a Bitmap, work with them, then save them to a Bitmap.
Using Bitmap.GetPixel() and Bitmap.SetPixel(), my program runs slowly.
How can I quickly convert Bitmap to byte[] and back?
I need a byte[] with length = (4 * width * height), containing RGBA data of each pixel.
You can do it a couple of different ways. You can use unsafe to get direct access to the data, or you can use marshaling to copy the data back and forth. The unsafe code is faster, but marshaling doesn't require unsafe code. Here's a performance comparison I did a while back.
Here's a complete sample using lockbits:
/*Note unsafe keyword*/
public unsafe Image ThresholdUA(float thresh)
{
Bitmap b = new Bitmap(_image);//note this has several overloads, including a path to an image
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);
/*This time we convert the IntPtr to a ptr*/
byte* scan0 = (byte*)bData.Scan0.ToPointer();
for (int i = 0; i < bData.Height; ++i)
{
for (int j = 0; j < bData.Width; ++j)
{
byte* data = scan0 + i * bData.Stride + j * bitsPerPixel / 8;
//data is a pointer to the first byte of the 3-byte color data
//data[0] = blueComponent;
//data[1] = greenComponent;
//data[2] = redComponent;
}
}
b.UnlockBits(bData);
return b;
}
Here's the same thing, but with marshaling:
/*No unsafe keyword!*/
public Image ThresholdMA(float thresh)
{
Bitmap b = new Bitmap(_image);
BitmapData bData = b.LockBits(new Rectangle(0, 0, _image.Width, _image.Height), ImageLockMode.ReadWrite, b.PixelFormat);
/* GetBitsPerPixel just does a switch on the PixelFormat and returns the number */
byte bitsPerPixel = GetBitsPerPixel(bData.PixelFormat);
/*the size of the image in bytes */
int size = bData.Stride * bData.Height;
/*Allocate buffer for image*/
byte[] data = new byte[size];
/*This overload copies data of /size/ into /data/ from location specified (/Scan0/)*/
System.Runtime.InteropServices.Marshal.Copy(bData.Scan0, data, 0, size);
for (int i = 0; i < size; i += bitsPerPixel / 8 )
{
double magnitude = 1/3d*(data[i] +data[i + 1] +data[i + 2]);
//data[i] is the first of 3 bytes of color
}
/* This override copies the data back into the location specified */
System.Runtime.InteropServices.Marshal.Copy(data, 0, bData.Scan0, data.Length);
b.UnlockBits(bData);
return b;
}
You can use Bitmap.LockBits method. Also if you want to use parallel task execution, you can use the Parallel class in System.Threading.Tasks namespace. Following links have some samples and explanations.
http://csharpexamples.com/fast-image-processing-c/
http://msdn.microsoft.com/en-us/library/dd460713%28v=vs.110%29.aspx
http://msdn.microsoft.com/tr-tr/library/system.drawing.imaging.bitmapdata%28v=vs.110%29.aspx
If you're on C# 8.0 I'll suggest to use the new Span<T> for higher efficiency.
Here's a rough implementation
public unsafe class FastBitmap : IDisposable
{
private Bitmap _bmp;
private ImageLockMode _lockmode;
private int _pixelLength;
private Rectangle _rect;
private BitmapData _data;
private byte* _bufferPtr;
public int Width { get => _bmp.Width; }
public int Height { get => _bmp.Height; }
public PixelFormat PixelFormat { get => _bmp.PixelFormat; }
public FastBitmap(Bitmap bmp, ImageLockMode lockMode)
{
_bmp = bmp;
_lockmode = lockMode;
_pixelLength = Image.GetPixelFormatSize(bmp.PixelFormat) / 8;
_rect = new Rectangle(0, 0, Width, Height);
_data = bmp.LockBits(_rect, lockMode, PixelFormat);
_bufferPtr = (byte*)_data.Scan0.ToPointer();
}
public Span<byte> this[int x, int y]
{
get
{
var pixel = _bufferPtr + y * _data.Stride + x * _pixelLength;
return new Span<byte>(pixel, _pixelLength);
}
set
{
value.CopyTo(this[x, y]);
}
}
public void Dispose()
{
_bmp.UnlockBits(_data);
}
}
You want LockBits. You can then extract the bytes you want from the BitmapData object it gives you.
There is another way that is way faster and much more convenient. If you have a look at the Bitmap constructors you will find one that takes and IntPtr as the last parameter. That IntPtr is for holding pixel data. So how do you use it?
Dim imageWidth As Integer = 1920
Dim imageHeight As Integer = 1080
Dim fmt As PixelFormat = PixelFormat.Format32bppRgb
Dim pixelFormatSize As Integer = Image.GetPixelFormatSize(fmt)
Dim stride As Integer = imageWidth * pixelFormatSize
Dim padding = 32 - (stride Mod 32)
If padding < 32 Then stride += padding
Dim pixels((stride \ 32) * imageHeight) As Integer
Dim handle As GCHandle = GCHandle.Alloc(pixels, GCHandleType.Pinned)
Dim addr As IntPtr = Marshal.UnsafeAddrOfPinnedArrayElement(pixels, 0)
Dim bitmap As New Bitmap(imageWidth, imageHeight, stride \ 8, fmt, addr)
What you have now is a simple Integer array and a Bitmap referencing the same memory. Any changes you make to the Integer array will be directly affecting the Bitmap. Let us try this with a simple brightness transform.
Public Sub Brightness(ByRef pixels() As Integer, ByVal scale As Single)
Dim r, g, b As Integer
Dim mult As Integer = CInt(1024.0f * scale)
Dim pixel As Integer
For i As Integer = 0 To pixels.Length - 1
pixel = pixels(i)
r = pixel And 255
g = (pixel >> 8) And 255
b = (pixel >> 16) And 255
'brightness calculation
'shift right by 10 <=> divide by 1024
r = (r * mult) >> 10
g = (g * mult) >> 10
b = (b * mult) >> 10
'clamp to between 0 and 255
If r < 0 Then r = 0
If g < 0 Then g = 0
If b < 0 Then b = 0
r = (r And 255)
g = (g And 255)
b = (b And 255)
pixels(i) = r Or (g << 8) Or (b << 16) Or &HFF000000
Next
End Sub
You may notice that I have used a little trick to avoid doing floating point math within the loop. This improves performance quite a bit.
And when you are done you need to clean up a little of course...
addr = IntPtr.Zero
If handle.IsAllocated Then
handle.Free()
handle = Nothing
End If
bitmap.Dispose()
bitmap = Nothing
pixels = Nothing
I have ignored the alpha component here but you are free to use that as well. I have thrown together a lot of bitmap editing tools this way. It is much faster and more reliable than Bitmap.LockBits() and best of all, it requires zero memory copying to start editing your bitmap.
Building on #notJim answer (and with help from https://web.archive.org/web/20141229164101/http://bobpowell.net/lockingbits.aspx), I developed the following that makes my life a lot easier in that I end up with an array of arrays that allows me to jump to a pixel by its x and y coordinates. Of course, the x coordinate needs to be corrected for by the number of bytes per pixel, but that is an easy extension.
Dim bitmapData As Imaging.BitmapData = myBitmap.LockBits(New Rectangle(0, 0, myBitmap.Width, myBitmap.Height), Imaging.ImageLockMode.ReadOnly, myBitmap.PixelFormat)
Dim size As Integer = Math.Abs(bitmapData.Stride) * bitmapData.Height
Dim data(size - 1) As Byte
Marshal.Copy(bitmapData.Scan0, data, 0, size)
Dim pixelArray(myBitmap.Height)() As Byte
'we have to load all the opacity pixels into an array for later scanning by column
'the data comes in rows
For y = myBitmap.Height - 1 To 0 Step -1
Dim rowArray(bitmapData.Stride) As Byte
Array.Copy(data, y * bitmapData.Stride, rowArray, 0, bitmapData.Stride)
'For x = myBitmap.Width - 1 To 0 Step -1
' Dim i = (y * bitmapData.Stride) + (x * 4)
' Dim B = data(i)
' Dim G = data(i + 1)
' Dim R = data(i + 2)
' Dim A = data(i + 3)
'Next
pixelArray(y) = rowArray
Next
Try this C# solution.
Create a winforms app for testing.
Add a Button and a PictureBox, and a click event and a form closing event.
Use the following code for your form:
public partial class Form1 : Form
{
uint[] _Pixels { get; set; }
Bitmap _Bitmap { get; set; }
GCHandle _Handle { get; set; }
IntPtr _Addr { get; set; }
public Form1()
{
InitializeComponent();
int imageWidth = 100; //1920;
int imageHeight = 100; // 1080;
PixelFormat fmt = PixelFormat.Format32bppRgb;
int pixelFormatSize = Image.GetPixelFormatSize(fmt);
int stride = imageWidth * pixelFormatSize;
int padding = 32 - (stride % 32);
if (padding < 32)
{
stride += padding;
}
_Pixels = new uint[(stride / 32) * imageHeight + 1];
_Handle = GCHandle.Alloc(_Pixels, GCHandleType.Pinned);
_Addr = Marshal.UnsafeAddrOfPinnedArrayElement(_Pixels, 0);
_Bitmap = new Bitmap(imageWidth, imageHeight, stride / 8, fmt, _Addr);
pictureBox1.Image = _Bitmap;
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < _Pixels.Length; i++)
{
_Pixels[i] = ((uint)(255 | (255 << 8) | (255 << 16) | 0xff000000));
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
_Addr = IntPtr.Zero;
if (_Handle.IsAllocated)
{
_Handle.Free();
}
_Bitmap.Dispose();
_Bitmap = null;
_Pixels = null;
}
}
Now, any edits you make to the array will automatically update the Bitmap.
You will need to call the refresh method on the picturebox to see these changes.